加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 为什么Task.Result在这种情况下不起作用?

发布时间:2020-12-16 01:56:58 所属栏目:百科 来源:网络整理
导读:它使用以下代码正常工作: private void button1_Click(object sender,EventArgs e){ Taskint t = test(5); Console.WriteLine(t.Result);}private Taskint test(int n){ return Task.Run(() = { return n; });} 但是,如果我使用异步方法包装测试方法,它不起
它使用以下代码正常工作:

private void button1_Click(object sender,EventArgs e)
{
    Task<int> t = test(5);
    Console.WriteLine(t.Result);
}

private Task<int> test(int n)
{
    return Task.Run(() => 
    {
        return n;
    });
}

但是,如果我使用异步方法包装测试方法,它不起作用:

private Task<int> test(int n)
{
    return Task.Run(() =>
    {
        return n;
    });
}

public async Task<int> wrap()
{
    return await test(5);
}

private void button1_Click(object sender,EventArgs e)
{
    Task<int> t = wrap();
    Console.WriteLine(t.Result);
}

表格失去了回应.如果我使用await,它会按预期工作.

UPDATE1:
这两个答案都是正确的,但我只能将其标记为答案.基于对这个问题的理解,我做了进一步的测试.我在wrap方法中使用了ConfigureAwait
保留继续在UI以外的线程中运行:

public async Task<int> wrap()
{
    return await test(5).ConfigureAwait(false);
}

它工作正常.然后我测试了这个:

public async Task<int> wrap()
{
    int i = await test(5).ConfigureAwait(false);
    int j = i + await test(3);
    return j;
}

它在我第一次单击按钮时工作,但在第二次单击时再次死锁.如果我在test(3)之后添加了ConfigureAwait(false),就像这样:

public async Task<int> wrap()
{
    int i = await test(5).ConfigureAwait(false);
    int j = i + await test(3).ConfigureAwait(false);
    return j;
}

它再次运作,但这对我来说没有意义.由于第一个ConfigureAwait(false),wrap()中的所有以下同步部分都应该在非UI线程上运行.我不明白为什么第二个ConfigureAwait(false)是必要的.

UPDATE2:

private Task<int> test(int n)
{
    return Task.Run(() =>
    {
        Console.WriteLine("test(" + n + "): " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        return n;
    });
}

public async Task<int> wrap()
{
    Console.WriteLine("1.wrap(): " + System.Threading.Thread.CurrentThread.ManagedThreadId);
    int i = await test(5).ConfigureAwait(false);
    Console.WriteLine("2.wrap(): " + System.Threading.Thread.CurrentThread.ManagedThreadId);
    int j = i + await test(3);
    Console.WriteLine("3.wrap(): " + System.Threading.Thread.CurrentThread.ManagedThreadId);
    return j;
}

private void button1_Click(object sender,EventArgs e)
{
    try
    {
        Console.WriteLine("1.button1_Click(): " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        var t = wrap();
        Console.WriteLine("2.button1_Click(): " + System.Threading.Thread.CurrentThread.ManagedThreadId);

        Console.WriteLine(t.Result);    
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }    
}

几次单击后,表单冻结,输出为:

1.button1_Click(): 8
1.wrap(): 8 
test(5): 13
2.wrap(): 8
2.button1_Click(): 8 
test(3): 13

令我惊讶的是,“2.wrap():”与“1.wrap():”在同一个线程中运行,而不是“test(5)”.似乎ConfigureAwait(false)之后的代码也可以跳回UI线程.

解决方法

这是一个死锁 – 在单线程调度程序中运行的两个任务正在等待彼此完成.

理解这两个非常重要的事情:

>默认情况下,await的结果返回到与其开始时相同的调度程序.
> UI在单线程事件循环上运行,并且在UI调用中可用于等待的立即调度程序是调度到此事件循环的调度程序.在此调度程序中,一次只能执行一个Task.

因此,您需要特别注意如何在UI调度程序内部执行的Task上调用阻塞方法.

在第二个示例中,对Result的调用正在等待包装中的继续设置完成.延迟计划在UI线程上运行,对Result的调用恰好是阻塞,因此两者都不会完成.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读