user6048670
user6048670

Reputation: 2887

Is async-await a necessary condition for making two independent tasks in my program run in parallel?

I'll try to illustrate my question by the pointless example you see below.

using System;
using System.IO;
namespace PointlessProgram
{
    class PointlessClass 
    {
        public static void WriteFooToTextFile ( )
        {
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\Users\Hillary\Documents\PointlessFolder\Foo.txt"))
            {
                file.Write("Foo");
            }
        }

        public static void WriteBarToTextFile ( )
        {
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\Users\Hillary\Documents\PointlessFolder\Bar.txt")) 
            {
                file.Write("Bar");
            }    
        }

        static void Main() 
        {
           WriteFooToTextFile(); // (A)
           WriteBarToTextFile(); // (B)    
        }
    }
}

Here, (B) does not need to be run after (A) because it does not depend on any output produced by (A), and neighter does (A) depend on (B). Let's suppose my computer has 2 processors and will devote all it's processing power to running this program. Will the compiler figure out that (A) and (B) can be run in parallel, or will it run them one after the other unless I explicitely write my code to tell the machine to not wait for (A) to finish before beginning to execute (B)? And if so, is async-await how I change this execution from

===
 A
---
 B
===

to

========
 A |  B  
========

???

What I find confusing about async-await is that you don't think in terms of partioning your program into independent tasks A,B, C, ...; you instead think in terms of "this task" and "everything else" and all you're doing is saying "everything else can keep running while this task runs".

Please help me understand.

Upvotes: 2

Views: 648

Answers (4)

Lemonseed
Lemonseed

Reputation: 1732

As your program is presented, "A" will run first, then "B" will run after "A" concludes. You could implement the async/await model, however, since we don't really care about the return values, a safer and more concise may would simply be to start each method call on a new thread. We can start off with:

static void Main() 
{
   Task.Run(() => WriteFooToTextFile()); // (A)
   Task.Run(() => WriteBarToTextFile()); // (B)    
}

(To utilize the Task object, we will have to add a using System.Threading.Tasks; directive.)

A further argument to utilize the task model, is that the async/await model is not multi-threading, but merely asynchronous execution on the same application thread -- so in order to best take advantage of both CPUs, you should be executing on separate threads.

Of course now with multi-threading, we must be cognizant of a potential race condition. Since no dependence exists between each task in your example, there should be no foreseeable implications for one task to complete first versus the other. However, we do not want the program to terminate until both threads have completed; additionally, we don't want exceptions from one process interfering with the other, but we'd ideally want to handle all exceptions at once. Therefore we might implement the calls in the following manner:

static void Main() 
{
    try
    {
        Task.WaitAll(new Task[] { 
            Task.Run(() => WriteFooToTextFile()),  // (A)
            Task.Run(() => WriteBarToTextFile())   // (B)
        });
    }
    catch (AggregateException e)
    {
        // Handle exception, display failure message, etc.
    }
}

Upvotes: 1

Mayura Vivekananda
Mayura Vivekananda

Reputation: 674

Your code is synchronous, so A is run then B is run. To run them at the same time you should use the Task.Run() command to execute them in a separate task, then await them completing.

So:

public static async Task Write()
{
    Task t1 = Task.Run(() => WriteFooToTextFile());
    Task t2 = Task.Run(() => WriteBarToTextFile());
    await Task.WhenAll(t1, t2);
}

with the usage of:

await PointlessClass.Write();

Note the signature of the write method is now async Task. You will have to make your event handler (or whatever triggers this code), also async.

As i3arnon mentions below, you don't have to make your code asynchronous to achieve the parallel operations. Its just generally done to stop the UI thread from blocking in WPF and WinForms applications, which is why you see the code going hand in hand in a lot of examples.

If you are using console, try:

static void Main()
{
  MainAsync().Wait();
}

static async Task MainAsync()
{
    Task t1 = Task.Run(() => WriteFooToTextFile());
    Task t2 = Task.Run(() => WriteBarToTextFile());
    await Task.WhenAll(t1, t2);
}

Refer to: Async Console App for more information.

Upvotes: 2

MrBlonde
MrBlonde

Reputation: 21

The above will run sequentially, imperative languages expect the developer to explicitly state the rules of execution. Most languages were initially designed to be run on single core system, they follow a Von Neumann Architecture.

https://en.wikipedia.org/wiki/Von_Neumann_architecture

So as a result, you're required to explicitly state what can safely be run in parallel. The compiler should be treated as if it's dumb.

In terms of ways to achieve a parallel task, you could use the likes of Parallel.Invoke. I'd probably just resort to Parallel invoke or Task.Run in the above instance of code.

Upvotes: 2

i3arnon
i3arnon

Reputation: 116586

Will the compiler figure out that (A) and (B) can be run in parallel?

No. The compiler doesn't automatically parallelize anything for you.

And if so, is async-await how I change this execution?

No as well. async-await doesn't really have anything to do with it.

You're code can be sequential whether it's synchronous or not. And it can be parallel whether it's asynchronous or not.

For example, synchronous and parallel:

var task1 = Task.Run(() => WriteFooToTextFile());
var task2 = Task.Run(() => WriteBarToTextFile());
Task.WaitAll(task1, task2);

And asynchronous and sequential:

await WriteFooToTextFileAsync();
await WriteBarToTextFileAsync();

By making your operations truly asynchronous you free up threads and allow them to work on other parts of your application. But if there aren't other parts they sit and wait in the ThreadPool and you don't gain much.

Upvotes: 5

Related Questions