13

I have a small query.

I admit that I haven't use multithreading much before .Net 4.5, but with the new async/await functionality I decided to give it a try. I started experimenting with it and all seems fine, but I wasn't able to find a solution for my "problem" anywhere on the web.

So everyone explain how await can be used with the newly transformed methods of the .Net platform (e.g. WriteAsync(), ReadAsync() etc...), but what if I wanted to use it for my own methods? For example, lets say that I am performing an extremely expensive calculation and want all of my 4 cores working on it. I would have something similar to this:

async Task DoLotsOfWork<T>(T[] data, int start, int end)
{
    //Do very expensive work
}

But since I don't have an await keyword there, the method is just treated as a synchronous one. I would like to call it 4 times from outside so that it can run on all of my cores, while I display something sensible to the user(e.g. "Please wait..."). The only solution that I was able to figure out was to add a await Task.Yield(); in the start of the method. Something like this:

async Task DoLotsOfWork<T>(T[] data, int start, int end)
{
    await Task.Yield();
    //Do very expensive work
}

In that case, the method would behave as I would expect it to. But is there a better solution for this? I feel that it should be easier/more sensible than writing exactly that line of code. I understand that I can create a Task/Thread object and call the Start() method, but that requires even more work. I just thought that with the new async/await functionality this sort of thing would be easier.

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Milcho
  • 329
  • 1
  • 3
  • 9
  • 4
    The new async / await pattern does not offer multithreading. It just offers cleaner code for handling operations that block your current thread. – Freeman Mar 07 '13 at 14:56
  • The keywords do not mean, "use all my cores". They just tell the compiler and the runtime that an operation can be performed on a sperate thread. If there a 4 operations that may be run concurrently they might be run on 4 different cores. – Jodrell Mar 07 '13 at 15:01
  • You want to take a look at this [asynchrony vs concurrency](http://stackoverflow.com/questions/4844637/what-is-the-difference-between-concurrency-parallelism-and-asynchronous-methods), async await DOES NOT help with multithreading. Its useful for even single cored machines. – Aron Mar 07 '13 at 15:05
  • Ok. So in other words - don't use async/await for multithreding. I get it now. Thank you. – Milcho Mar 07 '13 at 15:17
  • Its not that don't use, but rather you cannot use it for that. It was not meant to do that, so it cannot do it. – Freeman Mar 07 '13 at 15:17
  • 2
    You can most certainly use await in conjunction with multi-threaded code, you just need some mechanism to start running code in another thread, once you've done that through some other means, `async/await` can help you manage them more effectively. – Servy Mar 07 '13 at 15:20

1 Answers1

18

So to start with you'll need a method that does the very expensive work synchronously:

public void DoLotsOfWork<T>(T[] data, int start, int end) 
{
    Thread.Sleep(5000);//placeholder for real work
}

Then we can use Task.Run to start multiple instances of this work in a threadpool thread.

List<Task> tasks = new List<Task>();
for(int i = 0; i < 4; i++)
{
    tasks.Add(Task.Run(()=>DoLotsOfWork(data, start, end));
}

Then we can do a non-blocking wait until all of them are done:

await Task.WhenAll(tasks);
Servy
  • 202,030
  • 26
  • 332
  • 449
  • Alternatively, use the `Parallel` class, which *is* designed for parallel processing. – Stephen Cleary Mar 07 '13 at 17:17
  • 4
    @StephenCleary the issue there is that the parallel methods block until the results are done, so you end up needing to throw the `Parallel.For` or `Foreach` into a `Task.Run` if you are currently in, say, a UI context and can't block until the tasks complete. – Servy Mar 07 '13 at 17:19
  • 2
    @Servy: True. But the benefit of `Parallel` is it will automatically partition work to make the best use of your available cores. As you said, you can wrap it in `Task.Run` to get a `Task` representing parallel work. – Stephen Cleary Mar 07 '13 at 17:38
  • If from one of the `Task.Run(() => myFunction());` I have a myFunction() returning some value, how do I handle it? – Nitin Labhishetty Jul 09 '15 at 21:14
  • @codeln Have a `List>` and store the result of `await Task.Whenall` in a variable. – Servy Jul 10 '15 at 12:52