c#Task.WhenAll在等待完成时阻塞

时间:2019-05-29 08:32:40

标签: c# multithreading winforms task

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

我希望ReportOnTasks会阻塞,直到WaitAll完成为止。请有人能解释为什么不是这种情况吗?我还担心我的结构不正确。

编辑,以阐明我的意图: 我想在收到数据后立即发送打印作业。然后继续执行其他一些DB操作。完成所有打印操作后,我想更新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;
}

2 个答案:

答案 0 :(得分:4)

您不是await拨打ReportOnTasks()的电话
而且,您不能在ctor中await,因为它们不能异步。

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

类似这样的东西:

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();

答案 1 :(得分:0)

通常不建议在类构造函数中执行繁重的操作,但是我想您不会更改该部分,因此为了等待ReportOnTasks完成,您需要使其同步。

请注意,构造函数本身不支持异步/等待,因此无法将其标记为async

话虽如此,您不会真正将void ReportOnTasks标记为async来提高性能。此外,由于异常处理方面的问题,通常不可行,因此不建议将void方法标记为async

因此,您可以像Alex所示的那样推迟ReportOnTasks,也可以同步等待所有任务完成(这可以在ctor中完成)。

    public void ReportOnTasks()
    {
        Task.WhenAll(oPrintController.Tasks).GetAwaiter().GetResult(); //synchronously wait

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

但是,我不建议采用这种方法,因为实例创建将花费一些时间,最重要的是会阻塞UI线程-这通常表明某些东西真的很腥