Shoaib Ijaz
Shoaib Ijaz

Reputation: 5587

Windows Form gets stuck on multiple async tasks

I am trying to execute parallel methods, but Form gets stuck whenever I call them.

Please correct what I am doing wrong. Here is the Code:

public partial class Form1 : Form
{
   private async void button1_Click(object sender, EventArgs e)
   {
      var itemList = new List<string>() { "Field1", "Field2", "Field3" };

      await Task.WhenAll(itemList.Select(item =>
           new WorkToDo(item).StartWork()
           ));
    }
}

public class WorkToDo
{
    private string id;

    public WorkToDo(string id)
    {
        this.id = id;
    }

    public async Task<bool> StartWork()
    {
        Calculate();
        Analyze();
        SomeToDo();
        var result = Save();
        await Task.Delay(100);
        return result;
    }

    private bool Calculate()
    {
        //Some complex and time taking calculation will be here
        return true;
    }

    private bool Analyze()
    {
        //Some complex and time taking calculation will be here
        return true;
    }

    private bool SomeToDo()
    {
        //Some complex and time taking calculation will be here
        return true;
    }

    private bool Save()
    {
        //Some complex and time taking calculation will be here
        return true;
    }
}

Upvotes: 3

Views: 1962

Answers (3)

Servy
Servy

Reputation: 203802

The problem that you have is that StartWork claims to be asynchronous, and yet it isn't. It does all of it's work synchronously.

Marking a method as async doesn't make it asynchronous. it just allows you to use the await keyword from within that method. If you perform long running synchronous operations from an async method then that method is still going to be doing that work synchronously.

There are really two approaches to take here. If there are some number of the things done in StartWork that really are inherently asynchronous, then you need to wrap whatever synchronous CPU bound work you have in calls to Task.Run so that the synchronous work that you have can be done asynchronously in a thread pool thread.

If there is no inherently asynchronous operations you have to do in StartWork then make the method clearly synchronous. Have it return a boolean, not a Task, and adjust the name to reflect the fact that it is synchronous. Then have the caller that calls it use Task.Run to do that whole operation on a thread pool thread asynchronously.

Having StartWork falsely claim to be asynchronous, and then still using Task.Run to do the purportedly asynchronous work in another thread is going to be extremely confusing to other readers of your code, as there should be no reason to offload an asynchronous method to a non-UI thread.

Upvotes: 2

Depechie
Depechie

Reputation: 6142

You need to remember that normal async / await will still be performed on the UI thread.

So to be sure that a real long action is pushed to a background thread, you need to wrap it in Task.Run... like Task.Run(() => Task.WhenAll(tasks));

To complete the question a bit more ( seeing the other answer available ), Task.Run usage is not something to take lightly. It all depends on what sort of code needs to be wrapped. There is a good write up series on this up on the blog of Stephen Cleary here http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-even-in.html so take some time to go through this and see what fits your project.

Or look here for some other details from Stephen https://stackoverflow.com/a/18015586

Upvotes: 4

Eldho
Eldho

Reputation: 8263

IMHO, If you are using Async operation you don't need Task.Run() if you have Sync Task and do it asynchronously you need Task.Run()

If you are using normal synchronous process just return Task<T> and use this Task.Run(()) to use background thread to process. See this answer

private async void button1_Click(object sender, EventArgs e)
{
    var itemList = new List<string>() { "Field1", "Field2", "Field3" }; // more than 50 items 

    Task.Run(() => Task.WhenAll(tasks));
}


public class WorkToDo
{
 private string id;

 public WorkToDo(string id)
 {
     this.id = id;
 }

 public async Task<bool> StartWork()
 {
   var t1 = Calculate();
   var t2 = Analyze();
   var t3 = SomeToDo();

    //Assuming you need to do all this before you save
    // so wait for the all.

    await Task.WhenAll(t1,t2,t3);
    var result = await Save();
    return result;
}

private async Task<bool> Calculate()
{
    //Some complex and time taking calculation will be here
    //Assuming here you have some DoAsync() method
    return true;
}

private async Task<bool> Analyze()
{
    //Some complex and time taking calculation will be here
    return true;
}

private async Task<bool> SomeToDo()
{
    //Some complex and time taking calculation will be here
    return true;
}

private async Task<bool> Save()
{
    //Some complex and time taking calculation will be here
    return true;
}

Using WhenAll() has some advantage like propagating all error at once, see this

Upvotes: 1

Related Questions