多线程代码中的异步和等待问题

时间:2019-07-11 11:22:49

标签: c# multithreading winforms

我有一个简单的情况:

public partial class Form1 : Form {
  Task t;
  public Form1() {
     InitializeComponent();
     t = Prepare();
  }

  public Task waitT() {
     return Task.Factory.StartNew(() => {
        for (int i = 0; i < 5; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T works \n");
        }
     });
  }
  public Task waitT2() {
     return Task.Factory.StartNew(() => {
        for (int i = 0; i < 3; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T2 works \n");
        }
     });
  }

  public Task waitT3() {
     return Task.Factory.StartNew(() => {
        for (int i = 0; i < 2; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T3 works \n");
        }
     });
  }

  public async Task Prepare() {
     //await Task.Factory.StartNew(async () => {
        await waitT();
        Console.WriteLine("T DOne.");
        await waitT2();
        Console.WriteLine("T2 DOne.");
        await waitT3();
        Console.WriteLine("T3 DOne.");
    // });
  }

  protected override void OnPaint(PaintEventArgs e) {
     base.OnPaint(e);
     t.Wait();
     Console.WriteLine("Finish");
  }

}

简而言之,我想在构造函数中启动一个任务,然后等待Paint事件中的一些数据。问题是在wait()方法中执行被阻塞,并且永远不会调用代码Console.WriteLine(“ TDone。”)。我已经读过,异步方法的执行是第一次同步,我认为问题是这样的。 为了尝试解决该问题,我更改了功能代码,准备方法如下:

 public async Task Prepare() {
     return await Task.Factory.StartNew(async () => {
        await waitT();
        Console.WriteLine("T DOne.");
        await waitT2();
        Console.WriteLine("T2 DOne.");
        await waitT3();
        Console.WriteLine("T3 DOne.");
     });
  }

通过这种方式,情况发生了变化,并且不使用paint中的wait方法,执行立即传递给Console.WriteLine(“ Finish”)和Prepare进程正在运行。

可能是我犯了一个秘密错误,有人可以向我解释这样做的正确方法吗?

谢谢

2 个答案:

答案 0 :(得分:0)

您正陷入僵局,因为您从异步切换到同步,并且两个事物/进程具有不同的同步上下文。 您不能使构造函数异步,但可以将其包装到等待的任务中作为解决方法:

public Form1() {
     InitializeComponent();
     Task.WaitAll(Task.Run( async ()=> await Prepare()));
}

您实际上并不需要Task t变量。由于Prepare不会产生任何返回值,因此您可以等待它。我想您是通过这个例子来说明您遇到的一些问题。通常,如果您从asyncawait开始,则需要将其提升到GUI的最高级别。同步处理的任何中断都会导致死锁

编辑: 发表评论后:

  

我想创建一个长期运行的任务(构造函数示例错误),运行它,然后在需要结果时检查任务是否完成(例如在事件中)。

我建议在构造函数中启动任务:

t = Task.Run(async ()=> await Prepare());

并在必须创建OnPaint的{​​{1}}事件中等待它,以便它不会阻塞UI:

async

答案 1 :(得分:0)

[编辑] 我更改了代码,从构造函数中删除了任务分配,并在加载事件中将其移动:

   public partial class Form1 : Form {
  Task t;
  public Form1() {
     InitializeComponent();
  }

  protected override void OnLoad(EventArgs e) {
     base.OnLoad(e);
     t = Prepare();
  }

  public Task waitT() {
     return Task.Run(() => {
        for (int i = 0; i < 5; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T works \n");
        }
     });
  }
  public Task waitT2() {
     return Task.Run(() => {
        for (int i = 0; i < 3; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T2 works \n");
        }
     });
  }

  public Task waitT3() {
     return Task.Run(() => {
        for (int i = 0; i < 2; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T3 works \n");
        }
     });
  }

  public async Task Prepare() {
     await Task.Run(async () => {
        await waitT();
        Console.WriteLine("T DOne.");
        await waitT2();
        Console.WriteLine("T2 DOne.");
        await waitT3();
        Console.WriteLine("T3 DOne.");
     });
  }

  protected override void OnPaint(PaintEventArgs e) {
     base.OnPaint(e);
     t.Wait();
     Console.WriteLine("Finish");
  }

}

现在结果有所不同,我等待正确的3个wait方法结束,但是尽管3个方法结束,执行仍然在t.wait()中阻塞。 Console.WriteLine(“ Finish”);不被称为:(

[编辑2] 可能我已经解决了我的问题,并且我可以使用其他方法在构造函数中创建任务,此代码可以正常工作(这仅是带有事件的示例):

   public partial class Form1 : Form {
  Task t;
  public Form1() {
     InitializeComponent();
     t = Prepare();
  }

  protected override void OnLoad(EventArgs e) {
     base.OnLoad(e);
  }

  public Task waitT() {
     return Task.Run(() => {
        for (int i = 0; i < 5; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T works \n");
        }
     });
  }
  public Task waitT2() {
     return Task.Run(() => {
        for (int i = 0; i < 3; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T2 works \n");
        }
     });
  }

  public Task waitT3() {
     return Task.Run(() => {
        for (int i = 0; i < 2; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T3 works \n");
        }
     });
  }

  public Task Prepare() {
     return Task.Run(async () => {
        await waitT();
        Console.WriteLine("T DOne.");
        await waitT2();
        Console.WriteLine("T2 DOne.");
        await waitT3();
        Console.WriteLine("T3 DOne.");
     });
  }

  protected override void OnPaint(PaintEventArgs e) {
     base.OnPaint(e);
     t.Wait();
     Console.WriteLine("Finish");
  }

}