1

By looping through multiple instances of TaskCompletionSource in the synchronous method, more than 5 programs will become slow to execute, but in the asynchronous method you don't have this problem.

This is problematic code Using the synchronization method, the execution becomes slow, creating one in about 1 second

Parallel.For(1, 100, (index) =>
{
    System.Console.WriteLine("start:");
    var t = new TaskCompletionSource<string>();
    count++;

    System.Console.WriteLine("end:" + count + "\n");
    t.Task.Wait();
    System.Console.WriteLine("ended:");
});

This is No problem code,Execute very fast

Parallel.For(1, 100, async (index) =>
{
    System.Console.WriteLine("start:");
    var t = new TaskCompletionSource<string>();
    count++;

    System.Console.WriteLine("end:" + count + "\n");
    await t.Task;
    System.Console.WriteLine("ended:");
});
fstam
  • 669
  • 4
  • 20
汪泽林
  • 11
  • 2
  • It can be interesting for you [Task.Delay(..).Wait()](https://stackoverflow.com/questions/53648014/what-is-going-on-with-task-delay-wait) – Rekshino Jan 24 '19 at 07:51
  • 1
    TaskCompletionSource isn't slow. It's not running *at all*. It provides a Task whose state can be switched when some other code calls `.SetResult`. No code signals the TCS in your code so it never completes – Panagiotis Kanavos Jan 24 '19 at 08:22
  • 1
    The *second* snippet is the actual bug. It doesn't await anything in parallel, it fires of 100 tasks and never awaits for them. Those 100 tasks are still blocked and will never print `ended:` – Panagiotis Kanavos Jan 24 '19 at 08:23
  • Sorry, I didn't describe the problem clearly. The above is the pseudo code I wrote. I just want to restore the concurrent scene. I don't need to wait for the output "ended", call and create the TaskCompletionSource instance through the synchronous method. In the case of concurrency, create the instance more than My CPU core number will be slow, I have tried setting ThreadPool.SetMinThreads(100, 100); but this does not solve the problem at all. – 汪泽林 Jan 25 '19 at 03:32

1 Answers1

1

You have some misunderstanding on Parallel.For().

First of all, Parallel.For() is not designed for asynchronous tasks. It is already mentioned in many post:

Imagine in a kitchen, you have 5 cooks(thread), Parallel.For() is assigning each pending dish one by one to each cook, straight forward. But async-await, is assigning 'agreement promising I will make a dish' to cook, that's an appointment only thread kick start it and 'finish' the process.

That's why you get very fast response in async-await example. But this line:

System.Console.WriteLine("ended:");

is not printed. When the working Thread meet the await, this task ends.


Let's simplify the example you are using, TaskCompletionSource is not a good example for testing different between async-await and Parallel.For().

//Async
Parallel.For(1, 100, async (index) =>
{
    System.Console.WriteLine("start:" + index);
    await Task.Delay(1000);
    System.Console.WriteLine("ended:" + index);
});
//Task.Delay(2000).Wait();

Async-await one will never print ended: index in this case. Try adding a line Task.Delay(2000).Wait(); at the end, you can notice ended:index is finally printed.

Try reducing 1000 to 1 in delay, it may print some ended but it is NOT guarantee by the Parallel.for, which means you tasks are not waited at all inside the Parallel.for() threads, it is hold by another threads for async-await.

//Sync

Parallel.For(1, 100, (index) =>
{
    System.Console.WriteLine("start:" + index);
    Task.Delay(1000).Wait();
    System.Console.WriteLine("ended:" + index);
});

How about parallel, yes, it is 'slow', starting thread and task-scheduler take cost. But it is really telling you tasks are finished before the end of program.

MT-FreeHK
  • 2,462
  • 1
  • 13
  • 29
  • If _tasks are not waited at all_, then "ended.." should be printed immediately Or do you mean `Parallel.For` tasks? – Rekshino Jan 24 '19 at 08:30
  • @Rekshino, I adjust the wording, sorry for making confuse. – MT-FreeHK Jan 24 '19 at 08:35
  • 1
    @Rekshino MatrixTai's explanation is correct. `ended:` runs only if the TCS is signalled, which never is. The OP's second snippet will start 100 task, 4 or 8 at a time (depending on the number of cores) and never wait for them. It will finish almost immediatelly. The tasks themselves though will stop when they reach `await t.Task;` and never print `ended:` – Panagiotis Kanavos Jan 24 '19 at 08:57
  • @PanagiotisKanavos His answer is partly correct. _Async-await one will never print ended: index, which means you tasks are not waited at all inside the Parallel.for() scope_ refers to his snippet and it's wrong. it will print "ended" if we will wait __outside__ the `Parallel.For` loop. So in async code of MatrixTai in inside `Parallel.For` we will wait and print "ended", outside we don't wait. – Rekshino Jan 24 '19 at 09:15
  • @Rekshino, `await` is not `wait()`, if you `wait()` inside, then it is no longer async, but a synchronous job, that's the sync example. `await` inside the `Parallel.for()` simply mean `come back here when finished`, but it is meaningless, as the `Parallel.for` ended after kicking start all tasks. If you want to use `async-await`, simply use `for-loop` with `Task.WaitAll/WhenAll`. – MT-FreeHK Jan 24 '19 at 09:37
  • @MatrixTai `Parallel.For` with async lambda is finished, but the tasks, which was started with `Parallel.For` are still running and will print "ended", if you wait __outside__ of `Parallel.For`, e.g.with `Task.Delay(2000).Wait();` So the statement "ended" will never be reached __in your code__ is wrong. – Rekshino Jan 24 '19 at 09:42
  • @Rekshino, okay, I understand your concern, I clarify it a bit more. – MT-FreeHK Jan 24 '19 at 10:02
  • Sorry, I didn't describe the problem clearly. The above is the pseudo code I wrote. I just want to restore the concurrent scene. I don't need to wait for the output "ended", call and create the TaskCompletionSource instance through the synchronous method. In the case of concurrency, create the instance more than My CPU core number will be slow, I have tried setting ThreadPool.SetMinThreads(100, 100); but this does not solve the problem at all. – 汪泽林 Jan 25 '19 at 03:32
  • @汪泽林, you may want to use this https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallel.for?view=netframework-4.7.2#System_Threading_Tasks_Parallel_For_System_Int32_System_Int32_System_Threading_Tasks_ParallelOptions_System_Action_System_Int32_System_Threading_Tasks_ParallelLoopState__ , with `new ParallelOptions { MaxDegreeOfParallelism = 5 }` – MT-FreeHK Jan 25 '19 at 07:26