我正在学习异步/等待,并遇到了需要同步调用异步方法的情况。我怎样才能做到这一点?
异步方法:
public async Task<Customers> GetCustomers()
{
return await Service.GetCustomersAsync();
}
正常使用:
public async void GetCustomers()
{
customerList = await GetCustomers();
}
我尝试过使用以下内容:
Task<Customer> task = GetCustomers();
task.Wait()
Task<Customer> task = GetCustomers();
task.RunSynchronously();
Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)
我还尝试了来自 here 的建议,但是当调度程序处于暂停状态时它不起作用。
public static void WaitWithPumping(this Task task)
{
if (task == null) throw new ArgumentNullException(“task”);
var nestedFrame = new DispatcherFrame();
task.ContinueWith(_ => nestedFrame.Continue = false);
Dispatcher.PushFrame(nestedFrame);
task.Wait();
}
以下是调用 RunSynchronously
的异常和堆栈跟踪:
System.InvalidOperationException 消息:不能在未绑定到委托的任务上调用 RunSynchronously。 InnerException:空源:mscorlib StackTrace:
at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
at System.Threading.Tasks.Task.RunSynchronously()
at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 638
at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 233
at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 597
at System.Collections.Generic.List`1.ForEach(Action`1 action)
at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 625
at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
at System.Threading.ExecutionContext.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.Run()
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run(Window window)
at System.Windows.Application.Run()
at MyApplication.App.Main() in C:\Documents and Settings\...\MyApplication\obj\Debug\App.g.cs:line 50
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Task
更难; 3) 我正在将 GeneXus 与 MongoDB C# driver 集成,它仅异步公开一些方法
await
。我应该让 Microsoft 更改他们的语言吗?还是我应该放弃同步并接受混乱的数据结构? async
是癌症,而不是 GPL。一旦你拥有它,你就无法摆脱它。
这是我发现适用于所有情况(包括暂停调度员)的解决方法。这不是我的代码,我仍在努力完全理解它,但它确实有效。
可以使用以下方法调用它:
customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());
代码来自 here
public static class AsyncHelpers
{
/// <summary>
/// Execute's an async Task<T> method which has a void return value synchronously
/// </summary>
/// <param name="task">Task<T> method to execute</param>
public static void RunSync(Func<Task> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
synch.Post(async _ =>
{
try
{
await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
}
/// <summary>
/// Execute's an async Task<T> method which has a T return type synchronously
/// </summary>
/// <typeparam name="T">Return Type</typeparam>
/// <param name="task">Task<T> method to execute</param>
/// <returns></returns>
public static T RunSync<T>(Func<Task<T>> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
T ret = default(T);
synch.Post(async _ =>
{
try
{
ret = await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
return ret;
}
private class ExclusiveSynchronizationContext : SynchronizationContext
{
private bool done;
public Exception InnerException { get; set; }
readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
readonly Queue<Tuple<SendOrPostCallback, object>> items =
new Queue<Tuple<SendOrPostCallback, object>>();
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("We cannot send to our same thread");
}
public override void Post(SendOrPostCallback d, object state)
{
lock (items)
{
items.Enqueue(Tuple.Create(d, state));
}
workItemsWaiting.Set();
}
public void EndMessageLoop()
{
Post(_ => done = true, null);
}
public void BeginMessageLoop()
{
while (!done)
{
Tuple<SendOrPostCallback, object> task = null;
lock (items)
{
if (items.Count > 0)
{
task = items.Dequeue();
}
}
if (task != null)
{
task.Item1(task.Item2);
if (InnerException != null) // the method threw an exeption
{
throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
}
}
else
{
workItemsWaiting.WaitOne();
}
}
}
public override SynchronizationContext CreateCopy()
{
return this;
}
}
}
请注意这个答案是三年前的。我主要根据使用 .Net 4.0 的经验编写它,很少使用 4.5,尤其是使用 async-await
。一般来说,这是一个很好的简单解决方案,但它有时会破坏一些东西。请阅读评论中的讨论。
.Net 4.5
只需使用这个:
// For Task<T>: will block until the task is completed...
var result = task.Result;
// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();
请参阅:TaskAwaiter、Task.Result、Task.RunSynchronously
.Net 4.0
用这个:
var x = (IAsyncResult)task;
task.Start();
x.AsyncWaitHandle.WaitOne();
...或这个:
task.Start();
task.Wait();
.Result
在某些情况下会产生死锁
Result
可以easily cause deadlock in async
code,正如我在博客中所描述的那样。
很惊讶没有人提到这一点:
public Task<int> BlahAsync()
{
// ...
}
int result = BlahAsync().GetAwaiter().GetResult();
不像这里的其他一些方法那么漂亮,但它有以下好处:
它不会吞下异常(例如等待)
它不会包装在 AggregateException 中抛出的任何异常(如 Result)
适用于 Task 和 Task
此外,由于 GetAwaiter
是鸭子类型,这应该适用于从异步方法(如 ConfiguredAwaitable
或 YieldAwaitable
)返回的任何对象,而不仅仅是任务。
编辑:请注意,这种方法(或使用 .Result
)可能会死锁,除非您确保每次等待时都为所有可能达到的异步方法添加 .ConfigureAwait(false)
从 BlahAsync()
(不仅仅是它直接调用的)。 Explanation。
// In BlahAsync() body
await FooAsync(); // BAD!
await FooAsync().ConfigureAwait(false); // Good... but make sure FooAsync() and
// all its descendants use ConfigureAwait(false)
// too. Then you can be sure that
// BlahAsync().GetAwaiter().GetResult()
// won't deadlock.
如果您懒得到处添加 .ConfigureAwait(false)
,并且您不关心性能,您也可以这样做
Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
GetAwaiter()
,“此方法适用于编译器用户,而不是直接在代码中使用。”
在线程池上运行任务要简单得多,而不是试图欺骗调度程序同步运行它。这样你就可以确定它不会死锁。由于上下文切换,性能会受到影响。
Task<MyResult> DoSomethingAsync() { ... }
// Starts the asynchronous task on a thread-pool thread.
// Returns a proxy to the original task.
Task<MyResult> task = Task.Run(() => DoSomethingAsync());
// Will block until the task is completed...
MyResult result = task.Result;
Task.Run(DoSomethingAsync)
吗?这将删除一级代表。
Task<MyResult> task = Task.Run(async () => await DoSomethingAsync());
中的相反方向更明确,并解决了@sgnsajgon 的担忧,即它可能会返回一个任务<任务<MyResult>>。无论哪种方式都选择了正确的 Task.Run 重载,但异步委托使您的意图显而易见。
我正在学习异步/等待,并遇到了需要同步调用异步方法的情况。我怎样才能做到这一点?
最好的答案是你没有,细节取决于“情况”是什么。
它是属性获取器/设置器吗?在大多数情况下,拥有异步方法比“异步属性”更好。 (有关详细信息,请参阅 my blog post on asynchronous properties)。
这是一个 MVVM 应用程序并且您想要进行异步数据绑定吗?然后使用我的 NotifyTask
之类的东西,如我的 MSDN article on asynchronous data binding 中所述。
是构造函数吗?然后你可能想考虑一个异步工厂方法。 (有关详细信息,请参阅我的 blog post on asynchronous constructors)。
几乎总是有比同步异步更好的答案。
如果您的情况不可能(并且您可以通过在此处提出描述情况的问题来了解这一点),那么我建议只使用同步代码。一路异步是最好的;一路同步是第二好的。不推荐同步异步。
但是,在少数情况下需要异步同步。具体来说,您受到调用代码的限制,因此您必须同步(并且绝对没有办法重新思考或重新构建代码以允许异步),并且您必须调用异步代码。这是一种非常罕见的情况,但它确实会不时出现。
在这种情况下,您需要使用我在 brownfield async
development 上的文章中描述的一种技巧,特别是:
阻塞(例如,GetAwaiter().GetResult())。请注意,这可能会导致死锁(正如我在博客中所描述的那样)。
在线程池线程上运行代码(例如,Task.Run(..).GetAwaiter().GetResult())。请注意,这仅在异步代码可以在线程池线程上运行时才有效(即,不依赖于 UI 或 ASP.NET 上下文)。
嵌套消息循环。请注意,这仅在异步代码仅假定单线程上下文而不是特定上下文类型(许多 UI 和 ASP.NET 代码需要特定上下文)时才有效。
嵌套消息循环是所有 hack 中最危险的,因为它会导致 re-entrancy。重新进入非常难以推理,并且(IMO)是 Windows 上大多数应用程序错误的原因。特别是,如果您在 UI 线程上并且您在工作队列上阻塞(等待异步工作完成),那么 CLR 实际上会为您执行一些消息泵送 - 它实际上会处理一些 Win32 消息 从您的代码中。哦,你不知道哪些消息 - 当 Chris Brumme says "Wouldn’t it be great to know exactly what will get pumped? Unfortunately, pumping is a black art which is beyond mortal comprehension." 时,我们真的没有希望知道。
所以,当你在 UI 线程上这样阻塞时,你就是在自找麻烦。同一篇文章中的另一个 cbrumme 引用:“有时,公司内部或外部的客户会发现我们在 STA [UI 线程] 上的托管阻塞期间发送消息。这是一个合理的担忧,因为他们知道这非常困难编写面对重入的健壮代码。”
是的。 很难编写在可重入性面前稳健的代码。嵌套的消息循环强制您编写在重入面前稳健的代码。这就是 the accepted (and most-upvoted) answer for this question 在实践中极其危险的原因。
如果您完全没有其他选择-您无法重新设计代码,无法将其重组为异步-您被不可更改的调用代码强制同步-您无法将下游代码更改为同步- 你不能阻塞 - 你不能在单独的线程上运行异步代码 - 只有这样你才应该考虑接受重入。
如果您确实遇到了这种情况,我建议您使用 Dispatcher.PushFrame
for WPF apps 之类的东西,对于 WinForm 应用程序使用 Application.DoEvents
循环,对于一般情况,我自己的 AsyncContext.Run
。
Main()
方法的程序无法编译;在某些时候,您 必须 弥合同步和异步世界之间的差距。这不是“非常罕见的情况”,实际上在每个调用异步方法的程序中都是必需的。没有不“do sync-over-async”的选项,只有一个选项可以将这个负担分流给调用方法,而不是在你当前正在编写的方法中承担它。
Main
方法中需要异步同步。 ASP.NET、单元测试框架和每个 UI 系统都原生支持异步。即使您的所有应用程序都是控制台应用程序,您也只需要为每个应用程序执行一次异步同步。 (当然,不支持异步的库回调可能需要额外的 hack)。
async
。这很多。这不能只是默认值吗?
如果我正确地阅读了您的问题-希望同步调用异步方法的代码正在暂停的调度程序线程上执行。并且您希望实际同步阻塞该线程,直到异步方法完成。
C# 5 中的异步方法是通过在底层有效地将方法分成几部分来提供支持的,并返回一个可以跟踪整个 shabang 的整体完成情况的 Task
。但是,切碎的方法如何执行取决于传递给 await
运算符的表达式的类型。
大多数时候,您将在 Task
类型的表达式上使用 await
。 Task 对 await
模式的实现是“智能的”,因为它遵循 SynchronizationContext
,这基本上会导致以下情况发生:
如果进入 await 的线程在 Dispatcher 或 WinForms 消息循环线程上,则它确保异步方法的块作为消息队列处理的一部分发生。如果进入 await 的线程在线程池线程上,那么 async 方法的剩余块会出现在线程池的任何位置。
这就是您可能遇到问题的原因——异步方法实现试图在 Dispatcher 上运行其余的方法——即使它已暂停。
.... 备份! ……
我不得不问一个问题,为什么你试图同步阻塞异步方法?这样做会破坏为什么要异步调用该方法的目的。通常,当您开始在 Dispatcher 或 UI 方法上使用 await
时,您会希望将整个 UI 流异步化。例如,如果您的调用堆栈类似于以下内容:
[顶部] WebRequest.GetResponse() YourCode.HelperMethod() YourCode.AnotherMethod() YourCode.EventHandlerMethod() [UI 代码].Plumbing() - WPF 或 WinForms 代码 [消息循环] - WPF 或 WinForms 消息循环
然后,一旦将代码转换为使用异步,您通常会得到
[顶部] WebRequest.GetResponseAsync() YourCode.HelperMethodAsync() YourCode.AnotherMethodAsync() YourCode.EventHandlerMethodAsync() [UI 代码].Plumbing() - WPF 或 WinForms 代码 [消息循环] - WPF 或 WinForms 消息循环
实际回答
上面的 AsyncHelpers 类实际上可以工作,因为它的行为类似于嵌套的消息循环,但是它将自己的并行机制安装到 Dispatcher,而不是尝试在 Dispatcher 本身上执行。这是您的问题的一种解决方法。
另一种解决方法是在线程池线程上执行异步方法,然后等待它完成。这样做很容易 - 您可以使用以下代码段来完成:
var customerList = TaskEx.RunEx(GetCustomers).Result;
最终的 API 将是 Task.Run(...),但对于 CTP,您需要 Ex 后缀 (explanation here)。
TaskEx.RunEx(GetCustomers).Result
会挂起该应用程序。此外,GetCustomers() 方法通常是异步运行的,但是在一种情况下它需要同步运行,所以我一直在寻找一种方法来做到这一点,而无需构建该方法的同步版本。
async
方法;当然应该避免嵌套循环。
这对我来说效果很好
public static class TaskHelper
{
public static void RunTaskSynchronously(this Task t)
{
var task = Task.Run(async () => await t);
task.Wait();
}
public static T RunTaskSynchronously<T>(this Task<T> t)
{
T res = default(T);
var task = Task.Run(async () => res = await t);
task.Wait();
return res;
}
}
MyAsyncMethod().RunTaskSynchronously();
Task.Run
中是没有意义的。换句话说,在像 GetFromNetworkAsync().RunTaskSynchronously()
这样的正常用法中,UI 应用程序会挂起。
在 .Net 4.6 中测试。它还可以避免死锁。
对于返回 Task
的异步方法。
Task DoSomeWork();
Task.Run(async () => await DoSomeWork()).Wait();
对于返回 Task<T>
的异步方法
Task<T> GetSomeValue();
var result = Task.Run(() => GetSomeValue()).Result;
编辑:
如果调用者在线程池线程中运行(或者调用者也在一个任务中),在某些情况下仍然可能导致死锁。
Result
非常适合这项工作,否则非常危险。名称 Result
或 Result
的智能感知中没有任何内容表明它是阻塞调用。真的应该改名了。
我遇到过几次,主要是在单元测试或 Windows 服务开发中。目前我总是使用这个功能:
var runSync = Task.Factory.StartNew(new Func<Task>(async () =>
{
Trace.WriteLine("Task runSync Start");
await TaskEx.Delay(2000); // Simulates a method that returns a task and
// inside it is possible that there
// async keywords or anothers tasks
Trace.WriteLine("Task runSync Completed");
})).Unwrap();
Trace.WriteLine("Before runSync Wait");
runSync.Wait();
Trace.WriteLine("After runSync Waited");
这很简单,容易,我没有任何问题。
Task.Run(() => ..).Wait()
(稍作调整)。两者都应该工作。
我在 Microsoft.AspNet.Identity.Core 组件中找到了这段代码,它可以工作。
private static readonly TaskFactory _myTaskFactory = new
TaskFactory(CancellationToken.None, TaskCreationOptions.None,
TaskContinuationOptions.None, TaskScheduler.Default);
// Microsoft.AspNet.Identity.AsyncHelper
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
CultureInfo culture = CultureInfo.CurrentCulture;
return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(delegate
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap<TResult>().GetAwaiter().GetResult();
}
我发现在不阻塞 UI 线程的情况下同步运行任务的最简单方法是使用 RunSynchronously() ,例如:
Task t = new Task(() =>
{
//.... YOUR CODE ....
});
t.RunSynchronously();
就我而言,我有一个在发生某些事情时触发的事件。我不知道它会发生多少次。所以,我在我的事件中使用上面的代码,所以每当它触发时,它都会创建一个任务。任务是同步执行的,对我来说效果很好。考虑到它是多么简单,我花了这么长时间才发现它让我感到惊讶。通常,建议要复杂得多且容易出错。这是它简单而干净。
请注意 - 这种方法:
Task<Customer> task = GetCustomers();
task.Wait()
适用于 WinRT。
让我解释:
private void TestMethod()
{
Task<Customer> task = GetCustomers(); // call async method as sync and get task as result
task.Wait(); // wait executing the method
var customer = task.Result; // get's result.
Debug.WriteLine(customer.Name); //print customer name
}
public class Customer
{
public Customer()
{
new ManualResetEvent(false).WaitOne(TimeSpan.FromSeconds(5));//wait 5 second (long term operation)
}
public string Name { get; set; }
}
private Task<Customer> GetCustomers()
{
return Task.Run(() => new Customer
{
Name = "MyName"
});
}
此外,此方法仅适用于 Windows 应用商店解决方案!
注意:如果您在其他异步方法中调用您的方法,这种方式不是线程安全的(根据@Servy 的评论)
CancellationToken
。
在您的代码中,您第一次等待任务执行但您尚未启动它,因此它会无限期地等待。尝试这个:
Task<Customer> task = GetCustomers();
task.RunSynchronously();
编辑:
你说你得到一个例外。请发布更多详细信息,包括堆栈跟踪。
Mono contains 以下测试用例:
[Test]
public void ExecuteSynchronouslyTest ()
{
var val = 0;
Task t = new Task (() => { Thread.Sleep (100); val = 1; });
t.RunSynchronously ();
Assert.AreEqual (1, val);
}
检查这是否适合您。如果不是,虽然可能性很小,但您可能有一些奇怪的异步 CTP 构建。如果它确实有效,您可能需要检查编译器究竟生成了什么以及 Task
实例化与此示例有何不同。
编辑#2:
我检查了 Reflector,您描述的异常发生在 m_action
为 null
时。这有点奇怪,但我不是异步 CTP 方面的专家。正如我所说,您应该反编译您的代码并查看 Task
是如何被实例化的,以及它的 m_action
是如何成为 null
的。
RunSynchronously may not be called on a task unbound to a delegate
。谷歌没有帮助,因为所有结果都是中文的......
await
关键字时,任务由 async 方法创建。我之前的评论中发布的例外是我得到的例外,尽管它是少数几个我无法谷歌并找到原因或解决方案的例外之一。
async
和 async
关键字只不过是语法糖。编译器生成代码以在 GetCustomers()
中创建 Task<Customer>
,这就是我首先要看的地方。至于异常,您只发布了异常消息,没有异常类型和堆栈跟踪是没有用的。调用异常的 ToString()
方法并在问题中发布输出。
为什么不创建如下调用:
Service.GetCustomers();
那不是异步的。
注意:我认为最好的做法是不建议更改操作的性质,如果它是异步的,最好的事情就是按原样处理(一直异步)。通过这种方式,您可以获得其他好处,例如并行处理/多线程等。
看到其他答案没有使用这种方法,我也想在这里发布:
var customers = GetCustomersAsync().GetAwaiter().GetResult();
使用下面的代码片段
Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
正如许多人在评论中所说,简单地调用 .Result;
或 .Wait()
存在死锁风险。由于我们大多数人都喜欢 oneliners,因此您可以将它们用于 .Net 4.5<
通过异步方法获取值:
var result = Task.Run(() => asyncGetValue()).Result;
同步调用异步方法
Task.Run(() => asyncMethod()).Wait();
使用 Task.Run
不会出现死锁问题。
资源:
https://stackoverflow.com/a/32429753/3850405
此答案专为使用 WPF for .NET 4.5 的任何人设计。
如果您尝试在 GUI 线程上执行 Task.Run()
,如果您的函数定义中没有 async
关键字,那么 task.Wait()
将无限期挂起。
此扩展方法通过检查我们是否在 GUI 线程上来解决问题,如果是,则在 WPF 调度程序线程上运行任务。
在不可避免的情况下,此类可以充当 async/await 世界和非 async/await 世界之间的粘合剂,例如 MVVM 属性或对不使用 async/await 的其他 API 的依赖关系。
/// <summary>
/// Intent: runs an async/await task synchronously. Designed for use with WPF.
/// Normally, under WPF, if task.Wait() is executed on the GUI thread without async
/// in the function signature, it will hang with a threading deadlock, this class
/// solves that problem.
/// </summary>
public static class TaskHelper
{
public static void MyRunTaskSynchronously(this Task task)
{
if (MyIfWpfDispatcherThread)
{
var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { await task; });
result.Wait();
if (result.Status != DispatcherOperationStatus.Completed)
{
throw new Exception("Error E99213. Task did not run to completion.");
}
}
else
{
task.Wait();
if (task.Status != TaskStatus.RanToCompletion)
{
throw new Exception("Error E33213. Task did not run to completion.");
}
}
}
public static T MyRunTaskSynchronously<T>(this Task<T> task)
{
if (MyIfWpfDispatcherThread)
{
T res = default(T);
var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { res = await task; });
result.Wait();
if (result.Status != DispatcherOperationStatus.Completed)
{
throw new Exception("Error E89213. Task did not run to completion.");
}
return res;
}
else
{
T res = default(T);
var result = Task.Run(async () => res = await task);
result.Wait();
if (result.Status != TaskStatus.RanToCompletion)
{
throw new Exception("Error E12823. Task did not run to completion.");
}
return res;
}
}
/// <summary>
/// If the task is running on the WPF dispatcher thread.
/// </summary>
public static bool MyIfWpfDispatcherThread
{
get
{
return Application.Current.Dispatcher.CheckAccess();
}
}
}
我认为以下辅助方法也可以解决问题。
private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
{
TResult result = default(TResult);
var autoResetEvent = new AutoResetEvent(false);
Task.Run(async () =>
{
try
{
result = await func();
}
catch (Exception exc)
{
mErrorLogger.LogError(exc.ToString());
}
finally
{
autoResetEvent.Set();
}
});
autoResetEvent.WaitOne();
return result;
}
可以通过以下方式使用:
InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
这对我有用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp2
{
public static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static void RunSync(Func<Task> func)
{
_myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
}
class SomeClass
{
public async Task<object> LoginAsync(object loginInfo)
{
return await Task.FromResult(0);
}
public object Login(object loginInfo)
{
return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
//return this.LoginAsync(loginInfo).Result.Content;
}
}
class Program
{
static void Main(string[] args)
{
var someClass = new SomeClass();
Console.WriteLine(someClass.Login(1));
Console.ReadLine();
}
}
}
我发现 SpinWait 对此非常有效。
var task = Task.Run(()=>DoSomethingAsyncronous());
if(!SpinWait.SpinUntil(()=>task.IsComplete, TimeSpan.FromSeconds(30)))
{//Task didn't complete within 30 seconds, fail...
return false;
}
return true;
上述方法不需要使用.Result 或.Wait()。它还允许您指定超时,这样您就不会永远卡在任务永远无法完成的情况下。
我知道这是一个老问题,但我想分享我的解决方案,可能不是最好的,但可以完成工作:
//Declare an Event Handler:
private event EventHandler OnThemeApply;
//..somewhere in your code..
//we 'hear' the event handler
this.OnThemeApply += (object _Sender, EventArgs _E) =>
{
//Reaches here After the Async method had finished
this.OnThemeApply = null;
};
MyAsycMethod();
private void MyAsycMethod()
{
var t = System.Threading.Tasks.Task.Factory.StartNew(delegate
{
//Do something here
Invoke((MethodInvoker)(() =>
{
if(this.OnThemeApply != null) this.OnThemeApply(null, null); //<- Calls the Event
}));
});
}
在 wp8 上:
把它包起来:
Task GetCustomersSynchronously()
{
Task t = new Task(async () =>
{
myCustomers = await GetCustomers();
}
t.RunSynchronously();
}
叫它:
GetCustomersSynchronously();
private int GetSync()
{
try
{
ManualResetEvent mre = new ManualResetEvent(false);
int result = null;
Parallel.Invoke(async () =>
{
result = await SomeCalcAsync(5+5);
mre.Set();
});
mre.WaitOne();
return result;
}
catch (Exception)
{
return null;
}
}
或者你可以选择:
customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;
为此,请确保您引用扩展程序集:
System.Net.Http.Formatting
尝试以下对我有用的代码:
public async void TaskSearchOnTaskList (SearchModel searchModel)
{
try
{
List<EventsTasksModel> taskSearchList = await Task.Run(
() => MakeasyncSearchRequest(searchModel),
cancelTaskSearchToken.Token);
if (cancelTaskSearchToken.IsCancellationRequested
|| string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
{
return;
}
if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
{
RunOnUiThread(() => {
textViewNoMembers.Visibility = ViewStates.Visible;
taskListView.Visibility = ViewStates.Gone;
});
taskSearchRecureList = null;
return;
}
else
{
taskSearchRecureList = TaskFooterServiceLayer
.GetRecurringEvent(taskSearchList);
this.SetOnAdapter(taskSearchRecureList);
}
}
catch (Exception ex)
{
Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
}
}
DynamicNodeProviderBase
基类对其进行扩展时,不能将其声明为async
方法。要么我必须用一个新库替换,要么只调用一个同步操作。AspNetSynchronizationContext
,所以如果您调用这些 API,这个特定的 hack 将不起作用。