await whole goal is to add synchronism back to asynchronous code. This makes it easy to separate parts that occur synchronously and asynchronously. Your example is absurd in that it never gets any benefits from this - if you just called the method directly and did not Task.Run it in Task.Run and await ing, then you would have the same result (with less overhead).
Consider this, though:
await Task.WhenAll ( loadAsBinaryAsync(fileName1), loadAsBinaryAsync(fileName2), loadAsBinaryAsync(fileName3) );
Again, you have synchronization backward ( await functions as a synchronization barrier), but you performed three independent operations asynchronously with respect to each other.
Now there is no reason to do something similar in your code, since you are using Parallel.ForEach at the lower level - you are already using the CPU with the maximum (with unnecessary overhead, but do not pay attention to what is currently).
Thus, the basic use of await should actually handle asynchronous I / O, rather than working with the CPU - except for simplifying the code, which depends on the synchronization of some parts of the processor and some not (for example, you have four threads of execution that handle different parts of the problem at the same time, but at some point you need to reconnect to understand the individual parts - for example, look at the Barrier class). This includes things like “make sure that the user interface is not blocked when an intensive operation occurs with the processor in the background” - this makes the processor asynchronous with respect to the user interface. But at some point you still want to restore synchronization to make sure that you can display the results of the user interface.
Consider this winforms code snippet:
async void btnDoStuff_Click(object sender, EventArgs e) { lblProgress.Text = "Calculating..."; var result = await DoTheUltraHardStuff(); lblProgress.Text = "Done! The result is " + result; }
(note that the async void method, not the async Task and async Task<T> )
What happens is that (in the GUI thread) the label is first assigned the text Calculating... , then the asynchronous DoTheUltraHardStuff method DoTheUltraHardStuff scheduled, and then the method returns. Immediately. This allows the GUI thread to do whatever it needs. However, as soon as the asynchronous task is completed and the GUI is free to handle the callback, btnDoStuff_Click will continue with the result (or, of course, excluded) already indicated, back to the GUI stream, allowing you to set the shortcut to new text, including result of an asynchronous operation.
Asynchrony is not an absolute property - the material is asynchronous with some other things and synchronized with some other things. This only makes sense for some other things.
I hope you can now go back to the source code and understand the part that you misunderstood before. Of course, there are several solutions, but they depend a lot on how and why you are trying to do what you are trying to do. I suspect that you don’t need to use Task.Run or await at all - Parallel.ForEach already trying to distribute the CPU to several CPU cores, and the only thing you could do is make sure that the other code is not working, you do not need to wait for completion this work - which will make a lot of sense in a graphical application, but I don’t see how it would be useful in a console application for the sole purpose of computing this one thing.
So, you can really use await for the fire-and-forget code, but only as part of the code that does not prevent the execution of the code you want to continue. For example, you might have code like this:
Task<string> result = SomeHardWorkAsync(); Debug.WriteLine("After calling SomeHardWorkAsync"); DoSomeOtherWorkInTheMeantime(); Debug.WriteLine("Done other work."); Debug.WriteLine("Got result: " + (await result));
This allows SomeHardWorkAsync to execute asynchronously with respect to DoSomeOtherWorkInTheMeantime , but not with await result . And of course, you can use await in SomeHardWorkAsync without having to parse the asynchrony between SomeHardWorkAsync and DoSomeOtherWorkInTheMeantime .
The GUI example shown above simply takes advantage of the continuation processing as something that happens after the task is completed, while ignoring the Task created in the async method (in fact, this is not so much the difference between using async void and async Task when ignoring the result). So, for example, to start and forget your method, you can use the following code:
async void Fire(string filename) { var result = await ProcessFileAsync(filename); DoStuffWithResult(result); } Fire("MyFile");
This will cause the DoStuffWithResult to execute as soon as the result is ready, and the Fire method itself will return immediately after the ProcessFileAsync executed (up to the first await or any explicit return someTask ) ..
This template is usually not approved - there really is no reason to return void from the async method (except for event handlers); you could just as easily return Task (or even Task<T> depending on the scenario), and let the interlocutor decide whether he wants his code to be executed synchronously with respect to yours or not.
Yet again,
async Task FireAsync(string filename) { var result = await ProcessFileAsync(filename); DoStuffWithResult(result); } Fire("MyFile");
does the same as when using async void , except that the caller can decide what to do with the asynchronous task. Perhaps he wants to run two of them in parallel and continue everything after that? It can just await Task.WhenAll(Fire("1"), Fire("2")) . Or he just wants it all to happen completely asynchronously with respect to his code, so he just calls Fire("1") and ignores the resulting Task (of course, ideally, you at least want to handle possible exceptions).