Foo()之间有什么区别。结果和Task.Run(()=> Foo())。结果是C#?

问题描述 投票:2回答:2

在C#中这两个语句有什么区别?如果我在我的测试类中使用构造函数中的第一个,我会遇到死锁或类似的东西,测试永远不会完成。第二个代码可以运行。

// Deadlock.
var r = MyMethod().Result;

// Works.
var r = Task.Run(() => MyMethod()).Result;

更新:此提交中有更多上下文:https://github.com/webCRMdotcom/erp-integrations/pull/92/commits/dd8af89899ce1de837ef6e34f0688a685a5cea3b

c# asynchronous deadlock
2个回答
4
投票

不同之处在于起始线程上下文。

这是一个简单的样本

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {
        string r;
        OutputThreadInfo("Main");
        r = MyMethod().Result;
        r = Task.Run( () => MyMethod() ).Result;
    }

    public static async Task<string> MyMethod()
    {
        OutputThreadInfo("MyMethod");
        await Task.Delay(50);
        return "finished";
    }

    private static void OutputThreadInfo(string context)
    {
        Console.WriteLine("{0} {1}",context,System.Threading.Thread.CurrentThread.ManagedThreadId);
    }
}

.net fiddle

哪个会输出

Main 32
MyMethod 32
MyMethod 63

MyMethod的第一次调用将从与Main相同的线程开始,如果从具有同步上下文的线程启动,它将阻塞。

MyMethod的第二次调用将从另一个线程(来自线程池的工作线程)开始,作为Main,它没有同步上下文,因此不会阻塞。

PS你应该记住,控制台应用程序没有默认的同步上下文,但WinForms,WPF,UWP应用程序确实有,因此在async / await上的行为会有所不同


0
投票

Task.ResultTask.Wait阻止当前线程你应该使用await这个没有任何问题。 (虽然他们只是阻止,如果尚未完成)。

第二行将创建一个任务,并将在线程池中的可用线程上启动它的执行,这就是它不会阻塞的原因。 这是因为当与Task一起使用时,async-await构造将生成一个状态机,该状态机跟踪代码块中使用的所有等待,并且当完成所有等待时,它可以返回结果。请记住,根据您所在的同步上下文,await之后的代码可能在不同的线程上运行,然后是任务启动的线程。

所以当我必须执行同步异步方法时我会这样做我使用一小段代码,如下所示:

private static readonly TaskFactory _tf = new TaskFactory(
        CancellationToken.None, TaskCreationOptions.None, 
        TaskContinuationOptions.None, TaskScheduler.Default);

public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{      
  return _tf.StartNew<Task<TResult>>((Func<Task<TResult>>) (() =>
  {
    return func();
  })).Unwrap<TResult>().GetAwaiter().GetResult();
}

请记住,如果需要,您必须在CultureInfo RunSync任务工厂调用中使用相同的StarNew,这样您就不会遇到这类问题。

© www.soinside.com 2019 - 2024. All rights reserved.