提问人:Rachel 提问时间:2/24/2011 最后编辑:John SmithRachel 更新时间:6/27/2023 访问量:539638
如何同步运行异步 Task<T> 方法?
How would I run an async Task<T> method synchronously?
问:
我正在学习 async/await,并遇到了需要同步调用异步方法的情况。我该怎么做?
异步方法:
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)
我还尝试了这里的建议,但是当调度程序处于暂停状态时,它不起作用。
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
堆栈跟踪:
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()
答:
为什么不创建一个像这样的呼叫:
Service.GetCustomers();
那不是异步的。
评论
在您的代码中,您的第一个等待任务执行,但您尚未启动它,因此它会无限期地等待。试试这个:
Task<Customer> task = GetCustomers();
task.RunSynchronously();
编辑:
你说你得到了一个例外。请发布更多详细信息,包括堆栈跟踪。
Mono 包含以下测试用例:
[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 检查了您描述的异常发生在 .这有点奇怪,但我不是异步 CTP 方面的专家。正如我所说,你应该反编译你的代码,看看它是如何被实例化的。m_action
null
Task
m_action
null
评论
RunSynchronously may not be called on a task unbound to a delegate
await
async
而关键词只不过是语法糖。编译器生成要创建的代码,因此我首先要看的地方。至于异常,你只发布了异常消息,如果没有异常类型和堆栈跟踪,这是无用的。调用异常的方法并在问题中发布输出。async
Task<Customer>
GetCustomers()
ToString()
这是我发现的一种解决方法,适用于所有情况(包括暂停的调度员)。这不是我的代码,我仍在努力完全理解它,但它确实有效。
可以使用以下命令调用它:
customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());
代码来自这里
public static class AsyncHelpers
{
/// <summary>
/// Synchronously execute's an async Task method which has a void return value.
/// </summary>
/// <param name="task">The Task method to execute.</param>
public static void RunSync(Func<Task> task)
{
var oldContext = SynchronizationContext.Current;
var syncContext = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(syncContext);
syncContext.Post(async _ =>
{
try
{
await task();
}
catch (Exception e)
{
syncContext.InnerException = e;
throw;
}
finally
{
syncContext.EndMessageLoop();
}
}, null);
syncContext.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
}
/// <summary>
/// Synchronously execute's an async Task<T> method which has a T return type.
/// </summary>
/// <typeparam name="T">Return Type</typeparam>
/// <param name="task">The Task<T> method to execute.</param>
/// <returns>The result of awaiting the given Task<T>.</returns>
public static T RunSync<T>(Func<Task<T>> task)
{
var oldContext = SynchronizationContext.Current;
var syncContext = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(syncContext);
T result;
syncContext.Post(async _ =>
{
try
{
result = await task();
}
catch (Exception e)
{
syncContext.InnerException = e;
throw;
}
finally
{
syncContext.EndMessageLoop();
}
}, null);
syncContext.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
return result;
}
private class ExclusiveSynchronizationContext : SynchronizationContext
{
private readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
private readonly Queue<Tuple<SendOrPostCallback, object>> items =
new Queue<Tuple<SendOrPostCallback, object>>();
private bool done;
public Exception InnerException { get; set; }
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;
}
}
}
评论
DynamicNodeProviderBase
async
AspNetSynchronizationContext
如果我没看错你的问题 - 想要同步调用异步方法的代码正在挂起的调度程序线程上执行。并且您希望实际同步阻止该线程,直到异步方法完成。
C# 5 中的异步方法通过有效地将方法切成多个部分来支持,并返回一个可以跟踪整个 shabang 的整体完成情况。但是,切碎方法的执行方式可能取决于传递给运算符的表达式的类型。Task
await
大多数情况下,您将在 type 上使用 .Task 对模式的实现是“智能的”,因为它遵循 ,这基本上会导致以下情况发生:await
Task
await
SynchronizationContext
- 如果进入 的线程位于 Dispatcher 或 WinForms 消息循环线程上,则它确保异步方法的块作为消息队列处理的一部分发生。
await
- 如果进入 的线程位于线程池线程上,则异步方法的剩余块将出现在线程池上的任何位置。
await
这就是为什么您可能会遇到问题的原因 - 异步方法实现正在尝试在 Dispatcher 上运行其余部分 - 即使它已挂起。
....备份!....
我不得不问一个问题,你为什么要尝试同步阻止异步方法?这样做会破坏为什么要异步调用该方法的目的。通常,当您开始使用 Dispatcher 或 UI 方法时,您需要将整个 UI 流设置为异步。例如,如果您的调用堆栈如下所示:await
- [返回顶部]
WebRequest.GetResponse()
YourCode.HelperMethod()
YourCode.AnotherMethod()
YourCode.EventHandlerMethod()
[UI Code].Plumbing()
-WPF
或代码WinForms
- [Message Loop] - 或消息循环
WPF
WinForms
然后,一旦代码转换为使用异步,通常最终会得到
- [返回顶部]
WebRequest.GetResponseAsync()
YourCode.HelperMethodAsync()
YourCode.AnotherMethodAsync()
YourCode.EventHandlerMethodAsync()
[UI Code].Plumbing()
-WPF
或代码WinForms
- [Message Loop] - 或消息循环
WPF
WinForms
实际回答
上面的 AsyncHelpers 类实际上有效,因为它的行为类似于嵌套的消息循环,但它将自己的并行机制安装到 Dispatcher 中,而不是尝试在 Dispatcher 本身上执行。这是解决您问题的一种解决方法。
另一种解决方法是在线程池线程上执行异步方法,然后等待它完成。这样做很容易 - 您可以使用以下代码片段来完成:
var customerList = TaskEx.RunEx(GetCustomers).Result;
最终的 API 将是 Task.Run(...),但使用 CTP 时,您将需要 Ex 后缀(此处解释)。
评论
TaskEx.RunEx(GetCustomers).Result
async
请注意 - 这种方法:
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
请注意,这个答案已经三年了。我主要基于 .Net 4.0 的经验编写它,很少使用 4.5,尤其是 .
一般来说,这是一个不错的简单解决方案,但有时会破坏事情。请阅读评论中的讨论。async-await
.净 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
在某些情况下可能会产生死锁
在线程池上运行任务要简单得多,而不是试图欺骗调度程序同步运行它。这样你就可以确保它不会死锁。由于上下文切换,性能会受到影响。
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());
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;
}
}
我遇到过几次,主要是在单元测试或 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()
这对我来说效果很好
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()
此答案适用于使用 WPF for .NET 4.5 的任何人。
如果尝试在 GUI 线程上执行,则函数定义中没有关键字,则将无限期挂起。Task.Run()
task.Wait()
async
此扩展方法通过检查我们是否在 GUI 线程上,如果是,则在 WPF 调度程序线程上运行任务来解决此问题。
在不可避免的情况下,此类可以充当 async/await 环境和非异步/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();
}
}
}
我在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();
}
评论
惊讶的是,没有人提到这一点:
public Task<int> BlahAsync()
{
// ...
}
int result = BlahAsync().GetAwaiter().GetResult();
不像这里的其他一些方法那么漂亮,但它有以下好处:
- 它不会吞下异常(如
Wait
) - 它不会包装任何抛出的异常(如
AggregateException
Result
) - 两者都有效(自己试试吧!
Task
Task<T>
)
此外,由于是鸭子类型的,这应该适用于从异步方法(如 or )返回的任何对象,而不仅仅是 Tasks。GetAwaiter
ConfiguredAwaitable
YieldAwaitable
编辑:请注意,这种方法(或使用)可能会死锁,除非您确保在每次等待时都添加所有可能从中访问的异步方法(而不仅仅是它直接调用的方法)。解释。.Result
.ConfigureAwait(false)
BlahAsync()
// 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()
我认为以下辅助方法也可以解决问题。
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);
评论
我正在学习 async/await,并遇到了需要同步调用异步方法的情况。我该怎么做?
最好的答案是你没有,细节取决于“情况”是什么。
它是属性的 getter/setter 吗?在大多数情况下,异步方法比“异步属性”更好。(有关详细信息,请参阅我关于异步属性的博客文章)。
这是一个 MVVM 应用,并且您想要执行异步数据绑定吗?然后使用类似我的 NotifyTask
的东西,如我关于异步数据绑定的 MSDN 文章中所述。
是构造函数吗?然后,您可能需要考虑异步工厂方法。(有关详细信息,请参阅我关于异步构造函数的博客文章)。
几乎总是有比异步同步更好的答案。
如果您的情况无法做到这一点(并且您通过在此处提出描述情况的问题来了解这一点),那么我建议只使用同步代码。一路异步是最好的;一路同步是第二好的。不建议异步同步。
但是,在少数情况下,异步同步是必要的。具体来说,你受到调用代码的约束,因此你必须同步(并且绝对没有办法重新思考或重新构建你的代码以允许异步),并且你必须调用异步代码。 这是一种非常罕见的情况,但确实时有发生。
在这种情况下,您需要使用我关于棕地异步
开发的文章中描述的技巧之一,特别是:
- 阻塞(例如,)。请注意,这可能会导致死锁(正如我在博客中描述的那样)。
GetAwaiter().GetResult()
- 在线程池线程上运行代码(例如,)。请注意,仅当异步代码可以在线程池线程上运行(即不依赖于 UI 或 ASP.NET 上下文)时,这才有效。
Task.Run(..).GetAwaiter().GetResult()
- 嵌套消息循环。请注意,仅当异步代码仅假定单线程上下文而不是特定上下文类型(许多 UI 和 ASP.NET 代码需要特定上下文)时,这才有效。
嵌套消息循环是所有黑客中最危险的,因为它会导致重入。重入是非常棘手的推理,(IMO)是Windows上大多数应用程序错误的原因。具体而言,如果你在 UI 线程上,并且阻塞了工作队列(等待异步工作完成),则 CLR 实际上会为你执行一些消息提取 - 它实际上会从你的代码中处理一些 Win32 消息。哦,你不知道哪些消息 - 当克里斯·布鲁姆说“确切地知道什么会被抽走不是很好吗?不幸的是,抽水是一种超出凡人理解的黑色艺术“,那么我们真的没有希望知道。
所以,当你在UI线程上这样阻止时,你就是在自找麻烦。同一篇文章中的另一段话说:“公司内部或外部的客户不时会发现,我们在 STA [UI 线程] 上的托管阻止期间正在抽取消息。这是一个合理的担忧,因为他们知道,在面对重入时,很难写出健壮的代码。
是的,它是。很难写出在面对重入时健壮的代码。嵌套消息循环迫使您编写在面对重入时健壮的代码。这就是为什么这个问题被接受(和投票最多)的答案在实践中是极其危险的。
如果你完全没有其他选择——你不能重新设计你的代码,你不能把它重构为异步——你被不可更改的调用代码强制同步——你不能将下游代码更改为同步——你不能阻止——你不能在单独的线程上运行异步代码——那么,也只有这样,你才应该考虑接受重入。
如果您确实发现自己处于这个角落,我建议对 WPF 应用程序使用 Dispatcher.PushFrame
,对 WinForm 应用程序使用循环,对于一般情况,使用我自己的 AsyncContext.Run
。Application.DoEvents
评论
Main()
Main
async
使用以下代码片段
Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
评论
我发现在不阻塞 UI 线程的情况下同步运行任务的最简单方法是使用 RunSynchronously(),例如:
Task t = new Task(() =>
{
//.... YOUR CODE ....
});
t.RunSynchronously();
就我而言,我有一个在发生某些事情时触发的事件。我不知道它会发生多少次。因此,我在事件中使用了上面的代码,因此每当它触发时,它都会创建一个任务。任务是同步执行的,这对我来说效果很好。我只是感到惊讶的是,考虑到它是多么简单,我花了这么长时间才发现这一点。通常,建议要复杂得多,也容易出错。这是简单而干净的。
评论
正如许多人在评论中所说的那样,简单地打电话或存在僵局的风险。由于我们大多数人都喜欢单行,因此您可以将这些用于.Result;
.Wait()
.Net 4.5<
通过异步方法获取值:
var result = Task.Run(() => asyncGetValue()).Result;
异步调用异步方法
Task.Run(() => asyncMethod()).Wait();
不会因使用 而发生死锁问题。Task.Run
源:
https://stackoverflow.com/a/32429753/3850405
评论
这对我有用
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();
}
}
}
在 .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
我发现 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() 中。它还允许您指定超时,以便在任务永远无法完成的情况下不会永远卡住。
评论
注意:我认为,如果操作是异步的,则不建议更改操作的性质,因此最好的做法是按原样处理(始终异步)。通过这种方式,您可以获得其他好处,例如并行处理/多线程等。
看到其他答案没有使用这种方法,我也想在这里发布:
var customers = GetCustomersAsync().GetAwaiter().GetResult();
评论
var customer = GetCustomersAsync().Result
异步方法:
public async Task<Customers> GetCustomers()
{
return await Service.GetCustomersAsync();
}
这似乎对我有用:
Task.Run(GetCustomers).Wait();
评论
Task
await
async