在C#中这两个语句有什么区别?如果我在我的测试类中使用构造函数中的第一个,我会遇到死锁或类似的东西,测试永远不会完成。第二个代码可以运行。
// Deadlock.
var r = MyMethod().Result;
// Works.
var r = Task.Run(() => MyMethod()).Result;
更新:此提交中有更多上下文:https://github.com/webCRMdotcom/erp-integrations/pull/92/commits/dd8af89899ce1de837ef6e34f0688a685a5cea3b。
不同之处在于起始线程上下文。
这是一个简单的样本
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);
}
}
哪个会输出
Main 32 MyMethod 32 MyMethod 63
MyMethod的第一次调用将从与Main相同的线程开始,如果从具有同步上下文的线程启动,它将阻塞。
MyMethod的第二次调用将从另一个线程(来自线程池的工作线程)开始,作为Main,它没有同步上下文,因此不会阻塞。
PS你应该记住,控制台应用程序没有默认的同步上下文,但WinForms,WPF,UWP应用程序确实有,因此在async / await上的行为会有所不同
Task.Result
和Task.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
,这样您就不会遇到这类问题。