2

I got started using OTL for multithreading, and getting great helps!

A variety of using Parallel.ForEach have been successful. But now I encountered an unaccountable case.

Please see the simple and full code below:

program test;
{$APPTYPE CONSOLE}
uses
  OtlParallel;
var
  i: integer;
begin
  for i := 1 to 1251 do
    Parallel.ForEach(0, 0).Execute(
      procedure (const num: integer)
      begin
      end);
end.

When the iteration number exceeds 1250, an error occurs:

'System Error. Code: 1816. Not enough quota is available to process this command'.

Could I be misunderstanding any basic usage of OTL?

Bruno Toffolo
  • 1,504
  • 19
  • 24
TYK
  • 23
  • 5
  • 1
    The main thread is blocked from empty messages posted from the threads. See http://stackoverflow.com/q/25103602/576719. – LU RD Apr 17 '15 at 18:52
  • @LURD Thanks. I recognized that the link tells about a related issue, but it's a little bit hard for me. I always take advantage of ForEach with simple implementation like the sample above, and do not concern any messages. Is there any simple way to clean up the messages during ForEach by any chance? – TYK Apr 17 '15 at 20:08
  • Unfortunately I know too little about the inner workings of OTL to suggest a solution. I've seen a couple of similar questions lately, so @gabr perhaps can explain. – LU RD Apr 17 '15 at 20:27
  • Try calling `CheckSynchronize` after every call to `Parallel.ForEach()` in the loop. It would be a band aid if it works, not a solution. – LU RD Apr 17 '15 at 21:14
  • @LURD Thank you for your kind suggestion. I tried it, but regrettably it doens't seem to work. My code above is written as a console app to demonstrate with the simplest case, but I've tested with a VCL forms app. – TYK Apr 17 '15 at 21:37
  • 2
    Which version of Delphi are you using and which version of OTL? I was able to bump the limit up much higher before I ran into the 60 thread limit and not your limit. The way you have structured your loop doesn't make too much sense, though. What are you trying to accomplish? Traditionally you would use a ForEach loop like this `Parallel.ForEach(1,1251)`. – Graymatter Apr 17 '15 at 22:51
  • @Graymatter Thanks. I'm using Delphi XE4 and OTL 3.04. The example is just simplified to show the case. I'm actually running heavy tasks inside a ForEach with a proper range, and the tasks are different for each iteration up to 1251 or more. The actual code is like: for i := 1 to 2000 do begin {assign adequote tasks for i} Parallel.ForEach(0, NUMBEROFTHREADS - 1).Execute(procedure (const num: integer) begin {Run the assigned asks} end); end; – TYK Apr 18 '15 at 05:36

1 Answers1

2

That problem spurs from some (maybe questionable) design solutions inside the OTL and at the moment cannot be resolved (except by processing messages in the main thread, as others have stated).

In any case I would suggest that you further refactor your approach. You could, for example, create a BackgroundWorker abstraction with only one parallel worker and then create 2000 work items and send them to the BackgroundWorker. Inside the background worker's Execute method you can then use Parallel.ForEach to process a task.

Or you could create a BackgroundWorker with multiple parallel tasks (= number of cores) and then run a normal for loop in each task. That would probably give you the fastest performance.

BTW, if you are using Parallel.ForEach in its simple form, then the new Parallel.&For will perform better. It doesn't have all the bells and whistles of ForEach, but is considerably faster.

gabr
  • 26,580
  • 9
  • 75
  • 141
  • I tried using a BackgroundWorker: `BGWorker := Parallel.BackgroundWorker.NumTasks(1).Execute`, and then, `for i := 1 to 2000 do BGWorker.Schedule(BGWorker.CreateWorkItem(i));` But at the 1251st iteration it also causes the same error. Maybe am I wrong in following your instruction? – TYK Apr 18 '15 at 16:46
  • I might be missing an easy solution. Adding `Application.ProcessMessages;` below the `Parallel.ForEach` inside the iteration removed the error. Could this solution be a right way? I'm actually using `ForEach` with a class as an argument in my real code, but it can be also converted to using `For`. If `For` is much faster than `ForEach`, I hope to use it. But I'm sorry I couldn't find the instruction for using `Parallel.&For`. Could you please let me know where to find it? – TYK Apr 18 '15 at 18:14
  • @TYK Yes, others have already stated in comments to your question that processing messages will help. Also have I in the first paragraph of my answer. Paralell.For is documented in the source code. I'm pretty sure that I wrote an article about it but I can't find it anywhere on the net. Weird. When I find it, I'll post the link here. – gabr Apr 19 '15 at 09:31
  • Thank you so much @gabr. I don't have enough experience of win apps and have focused on mathematical purposes, so I didn't know how to handle such messages. Anyway I'm happy to have a solution now! I deeply appreciate your providing the valuable tool. – TYK Apr 19 '15 at 10:34
  • @TYK For the example of Parallel.For usage, look into the 57_For demo (part of the OTL repo/distribution). For a speed comparison with Parallel.ForEach, run the 58_ForVcForEach demo (without a debugger, it slows down thread creation a lot). – gabr Apr 19 '15 at 17:41