Rac*_*hel 606 c# asynchronous async-await c#-5.0
我正在学习async/await,并遇到了需要同步调用异步方法的情况.我怎样才能做到这一点?
异步方法:
public async Task<Customers> GetCustomers()
{
return await Service.GetCustomersAsync();
}
Run Code Online (Sandbox Code Playgroud)
正常使用:
public async void GetCustomers()
{
customerList = await GetCustomers();
}
Run Code Online (Sandbox Code Playgroud)
我尝试过使用以下内容:
Task<Customer> task = GetCustomers();
task.Wait()
Task<Customer> task = GetCustomers();
task.RunSynchronously();
Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)
Run Code Online (Sandbox Code Playgroud)
我也从这里尝试了一个建议,但是当调度程序处于暂停状态时它不起作用.
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();
}
Run Code Online (Sandbox Code Playgroud)
以下是调用的异常和堆栈跟踪RunSynchronously:
System.InvalidOperationException
消息:可能无法在未绑定到委托的任务上调用RunSynchronously.
InnerException:null
资料来源: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()
Run Code Online (Sandbox Code Playgroud)
Rac*_*hel 443
这是我发现适用于所有情况(包括暂停的调度程序)的解决方法.这不是我的代码,我仍然在努力完全理解它,但它确实有效.
它可以使用:
customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());
代码来自这里
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
AK_*_*AK_ 310
请注意,这个答案是三岁.我的写作主要基于.Net 4.0的体验,而4.5特别是async-await.一般来说,这是一个很好的简单解决方案,但它有时会破坏事物.请阅读评论中的讨论.
只要用这个:
// 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();
Run Code Online (Sandbox Code Playgroud)
请参阅: TaskAwaiter, Task.Result, Task.RunSynchronously
用这个:
var x = (IAsyncResult)task;
task.Start();
x.AsyncWaitHandle.WaitOne();
Run Code Online (Sandbox Code Playgroud)
...或这个:
task.Start();
task.Wait();
Run Code Online (Sandbox Code Playgroud)
Jam*_* Ko 116
惊讶没有人提到这个:
public Task<int> BlahAsync()
{
// ...
}
int result = BlahAsync().GetAwaiter().GetResult();
Run Code Online (Sandbox Code Playgroud)
不像这里的其他一些方法那么漂亮,但它有以下好处:
Wait)AggregateException(如Result)Task和Task<T>(自己尝试!)此外,由于GetAwaiter是duck-typed,这应该适用于从异步方法(如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.
Run Code Online (Sandbox Code Playgroud)
如果你懒得在.ConfigureAwait(false)任何地方添加,而你不关心性能,你可以选择做
Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
Run Code Online (Sandbox Code Playgroud)
Mic*_*rry 73
在线程池上运行任务要简单得多,而不是试图欺骗调度程序同步运行它.这样你就可以确定它不会死锁.由于上下文切换,性能受到影响.
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;
Run Code Online (Sandbox Code Playgroud)
Ste*_*ary 53
我正在学习async/await,并遇到了需要同步调用异步方法的情况.我怎样才能做到这一点?
最好的答案是你没有,细节取决于"情况"是什么.
它是属性getter/setter吗?在大多数情况下,拥有异步方法比"异步属性"更好.(有关更多信息,请参阅我关于异步属性的博客文章).
这是一个MVVM应用程序,您想要进行异步数据绑定吗?然后使用类似我的东西NotifyTask,如我的MSDN文章中关于异步数据绑定的描述.
它是构造函数吗?那么您可能想要考虑异步工厂方法.(有关更多信息,请参阅我在异步构造函数上的博客文章).
几乎总是有一个比同步异步更好的答案.
如果你的情况不可能(你通过在这里问一个描述情况的问题就知道了),那么我建议你只使用同步代码.一路异步是最好的; 一路同步是第二好的.建议不要使用同步异步.
但是,有一些情况需要同步异步同步.具体来说,您受到调用代码的约束,因此您必须同步(并且绝对无法重新思考或重新构造代码以允许异步),并且您必须调用异步代码.这是一种非常罕见的情况,但确实会不时出现.
在这种情况下,您需要使用我在棕色地带async开发文章中描述的其中一个黑客,具体来说:
GetAwaiter().GetResult()).请注意,这可能会导致死锁(正如我在博客中所述).Task.Run(..).GetAwaiter().GetResult()).请注意,只有异步代码可以在线程池线程上运行(即,不依赖于UI或ASP.NET上下文)时,这才有效.嵌套的消息循环是所有黑客中最危险的,因为它会导致重新入侵.Re-entrancy非常难以理解,而且(IMO)是Windows上大多数应用程序错误的原因.特别是,如果你在UI线程上并且阻塞工作队列(等待异步工作完成),那么CLR实际上会为你做一些消息 - 它实际上会处理你内部的一些Win32消息代码.噢,你不知道哪些消息 - 当克里斯布鲁姆 说"难道不知道究竟会有什么东西会被抽出来吗?不幸的是,抽水是一种超乎凡理解的黑色艺术." 那么我们真的没有希望知道.
所以,当你在UI线程上这样阻止时,你会遇到麻烦.另一篇文章引用了同一篇文章:"公司内部或外部的客户不时发现我们在STA [UI线程]的托管阻止期间正在抽取消息.这是一个合理的问题,因为他们知道这很难编写面对重入时强大的代码."
是的.很用心写的代码,在重入面对强劲.嵌套的消息循环迫使您编写在重入时面对强大的代码.这就是为什么接受(和最upvoted)答案这个问题是非常危险的做法.
如果你完全没有其他所有选项 - 你不能重新设计你的代码,你不能将它重组为异步 - 你可以通过不可更改的调用代码强制同步 - 你不能将下游代码更改为同步- 你不能阻止 - 你不能在一个单独的线程上运行异步代码 - 那么只有你才考虑采用重入.
如果你确实发现自己在这个角落,我会建议使用类似于Dispatcher.PushFrameWPF应用程序,循环使用Application.DoEventsWinForm应用程序,以及一般情况下,我自己的AsyncContext.Run.
Cle*_*ent 24
这对我很有用
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;
}
}
Run Code Online (Sandbox Code Playgroud)
The*_*ung 23
如果我正在读你的问题 - 希望同步调用异步方法的代码正在挂起的调度程序线程上执行.并且您希望实际上同步阻止该线程,直到异步方法完成.
C#5中的异步方法是通过有效地将方法切割成引擎盖下的部分来提供动力,然后返回一个Task可以跟踪整个shabang的整体完成情况.但是,切碎方法的执行方式取决于传递给await运算符的表达式的类型.
大多数情况下,您将使用await类型的表达式Task.任务对await模式的实现是"聪明的",因为它SynchronizationContext遵循,这基本上导致以下事件发生:
await位于Dispatcher或WinForms消息循环线程上,则它确保异步方法的块作为消息队列处理的一部分发生.await在线程池线程上,则异步方法的剩余块发生在线程池的任何位置.这就是为什么你可能遇到问题 - 异步方法实现试图在Dispatcher上运行其余部分 - 即使它已被暂停.
.... 备份!....
我不得不问这个问题,为什么你试图同步阻止异步方法?这样做会破坏为什么要异步调用该方法的目的.通常,当您开始使用awaitDispatcher或UI方法时,您将希望将整个UI流转为异步.例如,如果您的callstack类似于以下内容:
WebRequest.GetResponse()YourCode.HelperMethod()YourCode.AnotherMethod()YourCode.EventHandlerMethod()[UI Code].Plumbing()- WPF或WinForms代码WPF或WinForms消息循环然后,一旦代码转换为使用异步,您通常会最终得到
WebRequest.GetResponseAsync()YourCode.HelperMethodAsync()YourCode.AnotherMethodAsync()YourCode.EventHandlerMethodAsync()[UI Code].Plumbing()- WPF或WinForms代码WPF或WinForms消息循环实际上是回答
上面的AsyncHelpers类实际上是有效的,因为它的行为类似于嵌套的消息循环,但是它将自己的并行机制安装到Dispatcher而不是尝试在Dispatcher本身上执行.这是解决您问题的一种方法.
另一种解决方法是在线程池线程上执行异步方法,然后等待它完成.这样做很容易 - 您可以使用以下代码段执行此操作:
var customerList = TaskEx.RunEx(GetCustomers).Result;
Run Code Online (Sandbox Code Playgroud)
最终的API将是Task.Run(...),但是使用CTP你需要Ex后缀(这里有解释).
pix*_*xel 18
我发现同步运行任务而不阻塞UI线程的最简单方法是使用RunSynchronously(),如:
Task t = new Task(() =>
{
//.... YOUR CODE ....
});
t.RunSynchronously();
Run Code Online (Sandbox Code Playgroud)
在我的情况下,我有一个事件发生时会触发.我不知道会发生多少次.所以,我在我的事件中使用上面的代码,因此每当它触发时,它都会创建一个任务.任务同步执行,对我来说非常有用.我很惊讶我花了这么长时间才发现这是多么简单.通常,建议要复杂得多,容易出错.这是简单而干净的.
J. *_*non 16
我曾经面对过几次,主要是在单元测试或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");
Run Code Online (Sandbox Code Playgroud)
它简单,容易,我也没有问题.
wen*_*nhx 15
我在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();
}
Run Code Online (Sandbox Code Playgroud)
Rre*_*Cat 12
只是一点注意 - 这种方法:
Task<Customer> task = GetCustomers();
task.Wait()
Run Code Online (Sandbox Code Playgroud)
适用于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"
});
}
Run Code Online (Sandbox Code Playgroud)
此外,此方法仅适用于Windows应用商店解决方案!
注意:如果你在其他异步方法中调用你的方法,这种方式不是线程安全的(根据@Servy的注释)
在你的代码中,你的第一个等待任务执行但你还没有启动它所以它无限期地等待.试试这个:
Task<Customer> task = GetCustomers();
task.RunSynchronously();
Run Code Online (Sandbox Code Playgroud)
编辑:
你说你得到了一个例外.请发布更多详细信息,包括堆栈跟踪.
Mono 包含以下测试用例:
[Test]
public void ExecuteSynchronouslyTest ()
{
var val = 0;
Task t = new Task (() => { Thread.Sleep (100); val = 1; });
t.RunSynchronously ();
Assert.AreEqual (1, val);
}
Run Code Online (Sandbox Code Playgroud)
检查这是否适合您.如果不是,尽管不太可能,你可能会有一些奇怪的Async CTP版本.如果它确实有效,您可能想要检查编译器生成的确切内容以及Task实例化与此示例的不同之处.
编辑#2:
我用Reflector检查了你描述的异常是什么时候发生m_action的null.这有点奇怪,但我不是Async CTP的专家.就像我说的,你应该反编译你的代码,看看究竟是如何Task被实例化任何是怎么来m_action的null.
PS什么是偶尔的downvotes处理?注意详细说明?
为什么不创建一个如下调用:
Service.GetCustomers();
Run Code Online (Sandbox Code Playgroud)
那不是异步.
使用下面的代码片段
Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
Run Code Online (Sandbox Code Playgroud)
在.Net 4.6中测试。它还可以避免死锁。
对于异步方法return Task。
Task DoSomeWork();
Task.Run(async () => await DoSomeWork()).Wait();
Run Code Online (Sandbox Code Playgroud)
对于异步方法返回 Task<T>
Task<T> GetSomeValue();
var result = Task.Run(() => GetSomeValue()).Result;
Run Code Online (Sandbox Code Playgroud)
编辑:
如果调用方正在线程池线程中运行(或者调用方也处于任务中),则在某些情况下仍可能导致死锁。
注意:我认为,如果它是异步的,最好的做法是不建议更改操作的性质,最好的做法是按原样处理(一直异步)。通过这种方式,您可以获得其他好处,例如并行处理/多线程等。
看到其他答案没有使用这种方法,我也想在这里发布:
var customers = GetCustomersAsync().GetAwaiter().GetResult();
Run Code Online (Sandbox Code Playgroud)