【问题标题】:Wait synchronously for an async method to finish on the same thread同步等待异步方法在同一线程上完成
【发布时间】:2015-06-09 19:00:27
【问题描述】:

有没有办法同步等待在同一线程上运行的异步方法?

想要的效果是

  • 让 Worker() 在 UI 线程上异步运行
  • 同时等待Close()方法返回前完成

下面的例子进入了死锁,如果我让 Form1_FormClosing() 异步我不满足第二个条件。

public partial class Form1 : Form
{
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    CancellationTokenSource cts = new CancellationTokenSource();
    public Form1()
    {
        InitializeComponent();
        Show();
        Worker(cts.Token); // async worker started on UI thread
    }

    async void Worker(CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
            await TaskEx.Delay(1000);
        tcs.SetResult(true); // signal completition
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Close();
        MessageBox.Show("This is supposed to be second");
    }

    private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        cts.Cancel(); // request cancel 
        tcs.Task.Wait(); // deadlock
        await tcs.Task; // button1_Click() gets control back instead of Worker()
        MessageBox.Show("This is supposed to be first");
    }
}

【问题讨论】:

  • 使用await Task.Delay(1000, ct).ConfigureAwait(false)。和tcs.TrySetResult(true);。确保在Worker 中执行的任何长时间操作都可以通过令牌取消。还要在工作人员中捕获OperationCanceledException,在这种情况下捕获tcs.SetCanceled()
  • 您实际上想要做什么?您已经说过您的解决方案尝试有什么问题,但您还没有说明您要解决的问题是什么。
  • 你想让任务继续运行,还是在它暂停的任何中断点取消它?
  • @BenVoigt 我想在暂停的任何时候取消它。

标签: c# asynchronous async-await task deadlock


【解决方案1】:

有没有办法同步等待在同一线程上运行的异步方法?

您不需要同步等待。通过使用Workerasync Task 而不是async void,您可以获得所需的行为并删除无用的TaskCompletionSource

private Task workerTask;
public Form()
{
     workerTask = Worker(cts.Token);
}

private async Task Worker(CancellationToken ct)
{
    while (!ct.IsCancellationRequested)
        await TaskEx.Delay(1000);
}

private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    cts.Cancel(); // request cancel
    await workerTask; // Wait for worker to finish before closing
}

我错过了Close() 的实现,但我怀疑你可以不用它并转发表单关闭事件以取消工作人员。

【讨论】:

  • Close() 被另一个窗体调用,返回时必须停止 Worker(),否则被另一个窗体修改的东西会导致 Worker() 崩溃。亚历克斯的建议正是我想要的。当我请求取消时,任务会立即被终止,抛出异常,我不必担心 Worker() 会访问我将要修改的内容。
  • 如果你使用ct.Token.ThrowIfCancellationRequested,它只会被立即杀死。你在某处使用它吗?
  • 我没有,但它的行为与 Alex 建议的 Task.ConfigureAwait(false) 类似。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-19
  • 2014-08-09
  • 2013-02-15
相关资源
最近更新 更多