Windows窗体卡在多个异步任务上

时间:2016-02-09 13:05:18

标签: c# .net winforms async-await task

我正在尝试执行并行方法,但是Form随时都会被卡住 我称之为

请纠正我的错误。这是代码:

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;
    }
}

3 个答案:

答案 0 :(得分:4)

您需要记住,仍然会在UI线程上执行正常的async / await。

所以为了确保将一个真正的长动作推送到后台线程,你需要将它包装在Task.Run中...就像Task.Run(() => Task.WhenAll(tasks));

为了更多地完成这个问题(看到其他答案可用),Task.Run的使用并不是一件容易接受的事情。这一切都取决于需要包装的代码类型。 这里有一个很好的写作系列,在Stephen Cleary的博客上http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-even-in.html,所以花一些时间来完成这个,看看哪个适合你的项目。

或者在这里查看Stephen https://stackoverflow.com/a/18015586

的其他一些细节

答案 1 :(得分:2)

您遇到的问题是StartWork 声明是异步的,但它并非如此。它可以同步完成所有工作。

将方法标记为async并不会使其异步。它只允许您在该方法中使用await关键字。如果从async方法执行长时间运行的同步操作,那么该方法仍将同步执行该操作。

这里有两种方法。如果在StartWork中完成了一些本质上异步的事情,那么你需要在调用Task.Run时包装你所拥有的任何同步CPU绑定工作,这样你的同步工作就可以了在线程池线程中异步完成。

如果在StartWork中没有固有的异步操作,则使方法明确同步。让它返回一个布尔值,而不是Task,并调整名称以反映它是同步的事实。然后让调用它的调用者使用Task.Run以异步方式对线程池线程执行整个操作。

StartWork 错误地宣称是异步的,然后仍然使用Task.Run在另一个线程中执行据称的异步工作对代码的其他读者来说非常困惑,因为没有理由将异步方法卸载到非UI线程。

答案 2 :(得分:1)

恕我直言,如果你正在使用Async操作,如果你有Task.Run(),则不需要Sync Task并且异步执行,你需要Task.Run()

  

如果您使用的是正常的同步过程,只需返回Task<T>并使用此Task.Run(())来使用后台线程进行处理。见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;
}

使用WhenAll()有一些优势,例如一次传播所有错误,请参阅this