Recent

Author Topic: SOLVED: Dealing with STDOUT with ProcThreadPool.DoParallel  (Read 4672 times)

doropert

  • New member
  • *
  • Posts: 7
SOLVED: Dealing with STDOUT with ProcThreadPool.DoParallel
« on: September 22, 2017, 10:48:56 pm »
Hi everyone,

This is my first post for something that I could not find the answer in existing posts.

I need to run simultaneous calculations in parallel, which should print individual results via STDOUT. The following example works fine with me but I have a small problem:

When calculations are more complicated than just md5 and results are longer, threads fight for STDOUT causing interlaced/broken lines. Any ideas on how could I synchronize output so that threads don't collide?

Code: Pascal  [Select][+][-]
  1. program mt;
  2. {$mode DELPHI}
  3.  
  4. uses {$ifdef unix} cthreads, cmem, {$endif} Sysutils, md5, classes, MTProcs;
  5.  
  6. var
  7.     q: tstringlist;
  8.     i: integer;
  9.  
  10. procedure ParallelMD5 (Index: PtrInt; Data: Pointer; Item: TMultiThreadProcItem);
  11. begin
  12.     writeln('#',Index,'=',md5print(md5string(q[Index])));
  13. end;
  14.  
  15. begin
  16.  
  17.     // Load q with 10 strings
  18.     q:=tstringlist.create;
  19.     for i:=1 to 10 do q.add(inttostr(i));
  20.  
  21.     // Calculate MD5 hashes in a single thread
  22.     for i:=0 to 9 do writeln ('#',i,'=',md5print(md5string(q[i])));
  23.  
  24.     // Calculate MD5 hashes in 5 threads
  25.     ProcThreadPool.DoParallel(@ParallelMD5, 0, 9, nil, 5);
  26.  
  27. end.
  28.  

Last, this code must run on both Linux and Windows  ::)

Thank you all in advance!
« Last Edit: September 25, 2017, 09:40:42 am by doropert »

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: Dealing with STDOUT with ProcThreadPool.DoParallel
« Reply #1 on: September 22, 2017, 11:40:42 pm »
Use a TCriticalSection.

 you use the ENTER and if it has already been entered it'll make your thread wait.
when released, the ENTER will return and then that thread can use WRITELN..

 when that code is completed, you then use the LEAVE which will unwind the
lock allowing other threads to enter..

The only true wisdom is knowing you know nothing

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Dealing with STDOUT with ProcThreadPool.DoParallel
« Reply #2 on: September 23, 2017, 12:02:42 am »
Use writestr() to write it to a string, and add that string to a queue for the mainlist. (a tthreadlist if need be).

If the queue gets large enough, queue() a procedure  to the main thread to be written out. 

Alternately, you can try writing your own textfile implementation that buffers and only writes whole lines to the terminal. But it will be difficult to properly manage those buffers and flush them before thread end etc. For cracks only (and even then only for the kick, low added value)

The more traditional solution of adding to a list and queue() that is easier I think.
« Last Edit: September 23, 2017, 12:04:32 am by marcov »

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Dealing with STDOUT with ProcThreadPool.DoParallel
« Reply #3 on: September 23, 2017, 08:32:38 am »
The first thing I would do is make sure you flush stdout after the writeln. You may get way with that.. Flush is essential when using multithreaded IO to prevent broken output.
Example https://www.freepascal.org/docs-html/rtl/system/flush.html (It says writeln always causes a flush, but in my experience not in a multithreaded scenario on all platforms, specifically windows)
If that is not enough, I would prefer Marco's approach because it is less blocking than a critcal section.

Note that you are running on Windows? I seldom experience any problems on linux terminal writes, but it is a known problem on windows.
« Last Edit: September 23, 2017, 09:46:18 am by Thaddy »
Specialize a type, not a var.

doropert

  • New member
  • *
  • Posts: 7
Re: Dealing with STDOUT with ProcThreadPool.DoParallel
« Reply #4 on: September 23, 2017, 10:50:37 am »
Thank you all for your suggestions. Due to the ease of implementation and insignificant processing time of writing to STDOUT vs the calculations I need to make (in my actual application I spend a lot more time than just calculating an md5), I though I would try the TCriticalSection first. I also force the STDOUT flush.

I have simply created a TCriticalSection which I Enter and Leave before every writeln. However, threads still collide and I get corrupted output every now and then. 

Could someone please tell me what have I done wrong?  :-\

Code: Pascal  [Select][+][-]
  1. program mt;
  2. {$mode DELPHI}
  3.  
  4. uses {$ifdef unix} cthreads, cmem, {$endif} Sysutils, md5, classes, MTProcs;
  5.  
  6. var
  7.     MyCriticalSection: TRTLCriticalSection;
  8.     q: tstringlist;
  9.     i: integer;
  10.     F: Text;
  11.  
  12. procedure SyncWriteln (data: ansistring);
  13. begin
  14.     EnterCriticalSection(MyCriticalSection);
  15.     Try
  16.         Assign (F,'');
  17.         Rewrite (F);
  18.         writeln(F,data);
  19.         Flush(F);
  20.         Close(F);
  21.     Finally
  22.         LeaveCriticalSection(MyCriticalSection);
  23.     end;
  24. end;
  25.  
  26. procedure ParallelMD5 (Index: PtrInt; Data: Pointer; Item: TMultiThreadProcItem);
  27. begin
  28.     SyncWriteln('#'+inttostr(Index)+'='+md5print(md5string(q[Index])));
  29. end;
  30.  
  31. begin
  32.  
  33.     // Load q with 10 strings
  34.     q:=tstringlist.create;
  35.     for i:=1 to 10 do q.add(inttostr(i));
  36.  
  37.     // Calculate MD5 hashes in a single thread
  38.     for i:=0 to 9 do writeln ('#',i,'=',md5print(md5string(q[i])));
  39.  
  40.     // Calculate MD5 hashes in 5 threads
  41.     InitCriticalSection(MyCriticalSection);
  42.     ProcThreadPool.DoParallel(@ParallelMD5, 0, 9, nil, 5);
  43.     DoneCriticalSection(MyCriticalSection);
  44.  
  45. end.
  46.  
« Last Edit: September 23, 2017, 10:53:41 am by doropert »

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: Dealing with STDOUT with ProcThreadPool.DoParallel
« Reply #5 on: September 23, 2017, 11:11:29 am »
Use StdOut variable which is defined in System unit as threadvar. And for each thread, allocate separate text buffer so output wont get corrupted.

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Dealing with STDOUT with ProcThreadPool.DoParallel
« Reply #6 on: September 23, 2017, 12:26:19 pm »
Could someone please tell me what have I done wrong?  :-\
No idea. Are you saying that the snippet you pasted gives you distorted output ?

It should work, at least it does for me.

Do note that i don't manually open output though, i just use writeln.

Are you perhaps writing to output at another location (and forgot about it) ?

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: Dealing with STDOUT with ProcThreadPool.DoParallel
« Reply #7 on: September 23, 2017, 09:15:53 pm »
Looks like you are using the critical sections in the wrong place.

Use it in the parellelMD5 call instead.

 That  way you can completely build the string without it getting interrupted and
then call the writeln, etc,,,
The only true wisdom is knowing you know nothing

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Dealing with STDOUT with ProcThreadPool.DoParallel
« Reply #8 on: September 23, 2017, 09:30:28 pm »
Try flushing output (=stdout) before the paralel for, so

Code: Pascal  [Select][+][-]
  1. flush(output);
« Last Edit: September 23, 2017, 09:45:43 pm by marcov »

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Dealing with STDOUT with ProcThreadPool.DoParallel
« Reply #9 on: September 23, 2017, 09:36:13 pm »
Use it in the parellelMD5 call instead.
That's funny  ;D

doropert

  • New member
  • *
  • Posts: 7
Re: Dealing with STDOUT with ProcThreadPool.DoParallel
« Reply #10 on: September 25, 2017, 09:40:30 am »
Thank you all. The issue is SOLVED and the second code I posted addresses the issue (I was just having another problem in my actual implementation).

To summarize, in order to prevent STDOUT corruption in a multi-thread application where threads need to write to STDOUT, the formula that worked for me was:

1) Threads should not write to STDOUT directly, but instead call a function that prints inside a critical section, AND
2) STDOUT must be fushed before leaving that critical section

 

TinyPortal © 2005-2018