1

I have a lot of operations what i want to run async. I do:

var tasks = new List<Task<bool>>();

for(var i=0;i<1000;i++){
  tasks.Add(CreateGeoreferencedImageAsync(properties, scaleIndex, currentXmin, currentYmin, currentXmax, currentYmax));
}

while (tasks.Count > 0)
{
   var bunch = tasks.Take(4).ToList();
   bool[] firstFinishedTask = await Task.WhenAll(bunch);
   tasks.RemoveRange(0,4);
}

But i see that WhenAll execute all Task from tasks not only from bunch.
What i missed?

UPDATE

private Task<bool> CreateGeoreferencedImageAsync(ImageGenerationProperties 
properties, int scaleIndex,
        double currentXmin, double currentYmin, double currentXmax, double currentYmax)
    {
        return Task.Run(() =>
            {
                return CreateGeoreferencedImage(properties, scaleIndex, currentXmin, currentYmin, currentXmax,
                    currentYmax);
            });
    }
Kliver Max
  • 5,107
  • 22
  • 95
  • 148
  • 1
    Your list contains tasks that are already started. There is absolutely no reason to wait for a couple of them at a time. – FCin Oct 01 '18 at 11:48
  • So maybe the correct answer was that he should not create the Tasks with `Task.Run` but `new Task`. Maybe this is helpful: https://stackoverflow.com/questions/14075029/have-a-set-of-tasks-with-only-x-running-at-a-time – Tim Schmelter Oct 01 '18 at 11:53
  • Why don't you simply `Task.WaitAll(tasks)`? Whar are you trying to avoid? – Gabriel Oct 01 '18 at 11:54
  • I added method code. And i want to run just a part of Task couse i have not enough memory to run them all. – Kliver Max Oct 01 '18 at 11:57
  • 1
    Possible duplicate of [How to limit the amount of concurrent async I/O operations?](https://stackoverflow.com/questions/10806951/how-to-limit-the-amount-of-concurrent-async-i-o-operations) – Joe Phillips Oct 01 '18 at 11:58
  • @JoePhillips - no that is _not_ a (good) duplicate. This question is not about I/O, and that is crucial. – bommelding Oct 01 '18 at 12:01
  • Just use Microsoft's Dataflow api: https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/dataflow-task-parallel-library – zaitsman Oct 01 '18 at 12:02
  • @KliverMax - you are not very clear about what you want with the returned booleans. Just disregard them? – bommelding Oct 01 '18 at 12:04
  • @bommelding I got errors when used `void` and do not remove bool return value. – Kliver Max Oct 01 '18 at 12:07
  • So, there is no return? Are the GeoreferencedImages retrieved from one place and stored in another? Could any of that I/O be made async? – bommelding Oct 01 '18 at 12:09
  • I think that i understand idea that i gonna create new Tasks just after previous banch finish to work. – Kliver Max Oct 01 '18 at 12:18

2 Answers2

3

WhenAll doesn't execute the tasks. It waits for them to finish. The execution usually starts as soon as the task is created, in your case inside CreateGeoreferencedImageAsync.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
3

Forget about Tasks and especially about Task.Run().

This problem is best solved with Parallel.ForEach(). Or Linq's AsParallel().

Parallel.For(0, 1000, 
  new ParallelOptions { MaxDegreeOfParallelism = 4 },
  i => CreateGeoreferencedImage(properties, scaleIndex, 
      currentXmin, currentYmin, currentXmax,  currentYmax));
Joe Phillips
  • 49,743
  • 32
  • 103
  • 159
bommelding
  • 2,969
  • 9
  • 14
  • I believe this is an older way to do it. Semaphores would be the newer way. I could be wrong, too – Joe Phillips Oct 01 '18 at 11:59
  • 1
    Semaphores are way older than the Parallel class. About half a century. – bommelding Oct 01 '18 at 12:00
  • SemaphoreSlim isn't though. http://blog.briandrupieski.com/throttling-asynchronous-methods-in-csharp – Joe Phillips Oct 01 '18 at 12:02
  • Now is your chance to explain why to use TPL instead of the other options then. I think I understand why but I wouldn't expect others to – Joe Phillips Oct 01 '18 at 12:04
  • No, the question isn't finished yet. – bommelding Oct 01 '18 at 12:05
  • I agree this is the answer but I feel its important to explain why. My understanding is that it is a cpu bound (non async) "task" that is occurring. If it were actually async then semaphoreslim would make more sense here – Joe Phillips Oct 01 '18 at 12:19
  • @JoePhillips - it's probably not really CPU bound but as long as the interface of the core method is not awaitable, Tasks and WhenAll are a clumsy workaround. And I still don't see the use for semaphores here. With or without Tasks. – bommelding Oct 01 '18 at 12:33
  • @bommelding The main reason to use SemaphoreSlim here would be that Parallel.For will block threads. SemaphoreSlim can be used asynchronously. Maybe you can do the same with Parallel.For as well? – Joe Phillips Oct 01 '18 at 15:48
  • @JoePhillips - Yes, the Parallel.For() is blocking as a whole. When that is a problem, wrap it in a single Task.Run(). But there is nothing in the question that indicates that this is desired. – bommelding Oct 03 '18 at 06:26