首页 > 解决方案 > c# Task.WhenAll 在等待完成时阻塞

问题描述

我有一个简单的 Winforms 应用程序。我想后台 TCP 连接/打印请求,并在我的代码中的设定点检查所有任务的输出。

我希望ReportOnTasks阻止直到WaitAll完成。请有人能解释为什么不是这样吗?我也担心我的结构不正确。

编辑,澄清我的意图:我想在收到数据后立即发送打印作业。然后继续进行其他一些数据库操作。完成所有打印操作后,我想更新 UI 以说明结果。

我试图尽可能地简化代码。也许太多了。HomeController 只是初始化一些东西。表单和文件观察器上有触发主要功能的按钮。

public class HomeController 
{

    public HomeController(){

        MessageBox.Show("1");

        oPrintController.PrintAsync("192.168.2.213", Encoding.ASCII.GetBytes("string to print"));

        MessageBox.Show("2");

        // Block here untill tasks are complete
        ReportOnTasks();

        MessageBox.Show("Report on tasks complete");
    }


    public async void ReportOnTasks()
    {
        await Task.WhenAll(oPrintController.Tasks);

        foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
        {
            // do something with the result of task
        }
    }
}

PrintController

public class PrintController
{

    public List<Task<PrintResult>> Tasks = new List<Task<PrintResult>>();


    public async void PrintAsync(string sIP, List<byte[]> lsToPrint, int iPort = 9100)
    {
        var s = await Task.Run(() => PrintAsync1(sIP, lsToPrint));
    }

    public async System.Threading.Tasks.Task<PrintResult> PrintAsync1(string sIP, List<byte[]> lsToPrint, int iPort = 9100)
    {

        using (TcpClient tc = new TcpClient())
        {
            await tc.ConnectAsync(sIP, iPort);
            using (var ns = tc.GetStream())
            {
                foreach (byte[] btLabel in lsToPrint)
                {
                    await ns.WriteAsync(btLabel, 0, btLabel.Length);
                }
            }
        }

        Thread.Sleep(10000);

        return new PrintResult();
    }
}

public class PrintResult
{
    bool bSuccess = false;
}

标签: c#multithreadingwinformstask

解决方案


你没有await调用ReportOnTasks()
此外,你不能await在一个ctor中,因为它们不能是异步的。

根据您HomeController的使用方式,您可以使用一个静态异步方法,该方法返回一个HomeController由私有 ctor 创建的实例:

像这样的东西:

public class HomeController 
{

    //notice private - you can't new up a HomeController - you have to use `CreateInstance`
    private HomeController(){

        MessageBox.Show("1");

        //not clear from your code where oPrintController comes from??
        oPrintController.PrintAsync("192.168.2.213", Encoding.ASCII.GetBytes("string to print"));

        MessageBox.Show("2");

        MessageBox.Show("Report on tasks complete");
    }

    public static async Task<HomeController> CreateInstance() {
        var homeController = new HomeController();

        await homeController.ReportOnTasks();

        return homeController;
    }


    //don't use async void! Change to Task
    public async Task ReportOnTasks()
    {
        //not clear from your code where oPrintController comes from??
        await Task.WhenAll(oPrintController.Tasks);

        foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
        {
            // do something with the result of task
        }
    }
}

用法:

var homeControllerInstance = await HomeController.CreateInstance();

推荐阅读