C#async/await更新UI造成的死锁以及解决方案

  • Post author:
  • Post category:其他


async/await可以更新UI线程,但还是有一定问题存在,比如如下情况会造成死锁。

遇到的问题:

private void button1_Click(object sender, EventArgs e) {
    Task<string> res = GetResAsync();
    textBox1.Text = res.Result;
}

private async Task<string> GetResAsync() {
    string t = await Task.Run(() => {
        Thread.Sleep(1000);
        return "返回结果";
    });
    return t;
}

只要点击button就会造成线程死锁,原因:

await时会获取当前线程的SychronizationContext,而res.Result等待await执行完成,await等待获取SychronizationContext,造成死锁

解决方案1:在await前就把SychronizationContext获取了

缺点:

1、res.Result时会等待,容易造成界面无响应

2、SetSynchronizationContext(null)容易出现副作用

private void button1_Click(object sender, EventArgs e) {
    Task<string> res = GetResAsync();
    textBox1.Text = res.Result;
}

private async Task<string> GetResAsync() {
    //获取当前线程的SychronizationContext
    SynchronizationContext syncContext = WindowsFormsSynchronizationContext.Current;
    //设置当前线程的SychronizationContext为null
    WindowsFormsSynchronizationContext.SetSynchronizationContext(null);
    string t = await Task.Run(() => {
        Thread.Sleep(1000);
        return "返回结果";
    });
    //设置当前线程的SychronizationContext
    WindowsFormsSynchronizationContext.SetSynchronizationContext(syncContext);
    return t;
}

解决方案2:再套一层Task.Run

缺点:

1、res.Result时会等待,容易造成界面无响应

2、await无法直接更新UI界面

private void button1_Click(object sender, EventArgs e) {
    Task<string> res = Task.Run(GetResAsync);
    textBox1.Text = res.Result;
}

private async Task<string> GetResAsync() {
    string t = await Task.Run(() => {
        Thread.Sleep(1000);
        return "返回结果";
    });
    return t;
}



版权声明:本文为qq_42690327原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。