我将如何运行的异步任务&LT; T&GT;方法同步? [英] How would I run an async Task<T> method synchronously?
问题描述
我学习异步/在等待着,碰到了一个情况,我需要调用同步异步方法。我该怎么做?
异步方法:
公共异步任务&LT;客户和GT; GetCustomers的()
{
返回等待Service.GetCustomersAsync();
}
正常使用的情况:
公共异步无效GetCustomers的()
{
customerList = GetCustomers的等待();
}
我用下面的尝试:
任务&LT;客户&GT;任务= GetCustomers的();
task.Wait()任务&LT;客户&GT;任务= GetCustomers的();
task.RunSynchronously();任务&LT;客户&GT;任务= GetCustomers的();
而(task.Status!= TaskStatus.RanToCompletion)
我也尝试从一个建议<一href=\"http://social.msdn.microsoft.com/Forums/en/async/thread/163ef755-ff7b-4ea5-b226-bbe8ef5f4796\">here,然而,当调度员处于暂停状态下,它不工作。
公共静态无效WaitWithPumping(该任务的任务)
{
如果(任务== NULL)抛出新的ArgumentNullException(任务);
VAR nestedFrame =新DispatcherFrame();
task.ContinueWith(_ =&GT; nestedFrame.Continue = FALSE);
Dispatcher.PushFrame(nestedFrame);
task.Wait();
}
下面是调用异常和堆栈跟踪 RunSynchronously
:
System.InvalidOperationException
消息:RunSynchronously可能不会被要求绑定到委托的任务
的InnerException :空
源代码:mscorlib程序
堆栈跟踪
块引用>在System.Threading.Tasks.Task.InternalRunSynchronously(的TaskScheduler调度)
在System.Threading.Tasks.Task.RunSynchronously()
在MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList()在C:\\的Documents and Settings \\ ... \\ MyApplication.CustomControls \\控件\\ MyCustomControl.xaml.cs:638线
在MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels()在C:\\的Documents and Settings \\ ... \\ MyApplication.CustomControls \\控件\\ MyCustomControl.xaml.cs:233线
在MyApplication.CustomControls.Controls.MyCustomControl&LT; CreateOpenPanelList&GT; b__36(DesktopPanel面板)在C:\\的Documents and Settings \\ ... \\ MyApplication.CustomControls \\控件\\ MyCustomControl.xaml.cs:597线
在System.Collections.Generic.List`1.ForEach(Action`1动作)
在MyApplication.CustomControls.Controls.MyCustomControl&LT; CreateOpenPanelList&GT; d__3b.MoveNext()在C:\\的Documents and Settings \\ ... \\ MyApplication.CustomControls \\控件\\ MyCustomControl.xaml.cs:625线
在System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object州)
在System.Windows.Threading.ExceptionWrapper.InternalRealCall(代表回调,对象指定参数时,的Int32 numArgs)
在MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(对象源,委托方法,对象指定参数时,的Int32 numArgs,代表catchHandler)
在System.Windows.Threading.DispatcherOperation.InvokeImpl()
在System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(对象状态)
在System.Threading.ExecutionContext.runTry code(用户数据对象)
在System.Runtime.CompilerServices.RuntimeHelpers.Execute codeWithGuaranteedCleanup(试行code code,清理code撤销code,对象用户数据)
在System.Threading.ExecutionContext.RunInternal(ExecutionContext中的ExecutionContext,ContextCallback回调,对象状态)
在System.Threading.ExecutionContext.Run(ExecutionContext中的ExecutionContext,ContextCallback回调,对象状态,布尔ignoreSyncCtx)
在System.Threading.ExecutionContext.Run(ExecutionContext中的ExecutionContext,ContextCallback回调,对象状态)
在System.Windows.Threading.DispatcherOperation.Invoke()
在System.Windows.Threading.Dispatcher.ProcessQueue()
在System.Windows.Threading.Dispatcher.WndProcHook(IntPtr的HWND,味精的Int32,IntPtr的的wParam,lParam中的IntPtr,布尔和放大器;处理)
在MS.Win32.HwndWrapper.WndProc(IntPtr的HWND,味精的Int32,IntPtr的的wParam,lParam中的IntPtr,布尔和放大器;处理)
在MS.Win32.HwndSubclass.DispatcherCallbackOperation(对象o)
在System.Windows.Threading.ExceptionWrapper.InternalRealCall(代表回调,对象指定参数时,的Int32 numArgs)
在MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(对象源,委托方法,对象指定参数时,的Int32 numArgs,代表catchHandler)
在System.Windows.Threading.Dispatcher.InvokeImpl(优先级的DispatcherPriority,时间跨度超时,代表法,对象指定参数时,的Int32 numArgs)
在MS.Win32.HwndSubclass.SubclassWndProc(IntPtr的HWND,味精的Int32,IntPtr的的wParam,lParam中的IntPtr)
在MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG&安培; MSG)
在System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame帧)
在System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame帧)
在System.Windows.Threading.Dispatcher.Run()
在System.Windows.Application.RunDispatcher(对象忽略)
在System.Windows.Application.RunInternal(窗窗)
在System.Windows.Application.Run(窗窗)
在System.Windows.Application.Run()
在MyApplication.App.Main()在C:\\的Documents and Settings \\ ... \\ MyApplication的\\ OBJ \\调试\\ App.g.cs:行50
在System.AppDomain._nExecuteAssembly(RuntimeAssembly组装,字串[] args)
在System.AppDomain.ExecuteAssembly(字符串assemblyFile,证据assemblySecurity,字串[] args)
在Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
在System.Threading.ThreadHelper.ThreadStart_Context(对象状态)
在System.Threading.ExecutionContext.Run(ExecutionContext中的ExecutionContext,ContextCallback回调,对象状态,布尔ignoreSyncCtx)
在System.Threading.ExecutionContext.Run(ExecutionContext中的ExecutionContext,ContextCallback回调,对象状态)
在System.Threading.ThreadHelper.ThreadStart()
解决方案下面是一个解决办法,我发现,对所有案件(包括暂停的调度员)的作品。这不是我的code和我还在充分了解它,但它确实工作。
可以使用被称为:
customerList = AsyncHelpers.RunSync&LT;名单,LT;客户&GT;&GT;(()=&GT; GetCustomers的());
code是<一个href=\"http://social.msdn.microsoft.com/Forums/en/async/thread/163ef755-ff7b-4ea5-b226-bbe8ef5f4796\">here
公共静态类AsyncHelpers
{
///&LT;总结&gt;
///执行是一个异步任务&LT; T&GT;其中有一个void返回值同步方法
///&LT; /总结&gt;
///&LT; PARAM NAME =任务&GT;任务&LT; T&GT;方法来执行&LT; /参数&GT;
公共静态无效RunSync(Func键&LT;任务&GT;任务)
{
VAR oldContext = SynchronizationContext.Current;
VAR同步=新ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(同步);
synch.Post(异步_ =&GT;
{
尝试
{
等待任务();
}
赶上(例外五)
{
synch.InnerException = E;
扔;
}
最后
{
synch.EndMessageLoop();
}
}, 空值);
synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext);
} ///&LT;总结&gt;
///执行是一个异步任务&LT; T&GT;它具有同步一件T返回类型的方法
///&LT; /总结&gt;
///&LT; typeparam NAME =T&gt;返回类型&lt; / typeparam&GT;
///&LT; PARAM NAME =任务&GT;任务&LT; T&GT;方法来执行&LT; /参数&GT;
///&LT;&回报GT;&LT; /回报&GT;
公共静态ŧRunSync&LT; T&GT;(Func键&LT;任务&LT; T&GT;&GT;任务)
{
VAR oldContext = SynchronizationContext.Current;
VAR同步=新ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(同步);
ŧRET =默认(T);
synch.Post(异步_ =&GT;
{
尝试
{
RET =等待任务();
}
赶上(例外五)
{
synch.InnerException = E;
扔;
}
最后
{
synch.EndMessageLoop();
}
}, 空值);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
返回RET;
} 私有类ExclusiveSynchronizationContext:的SynchronizationContext
{
私人BOOL完成的;
公共异常的InnerException {搞定;组; }
只读的AutoResetEvent workItemsWaiting =新的AutoResetEvent(假);
只读队列&LT元组LT; SendOrPostCallback,对象&gt;&GT;项目=
新的问答LT元组LT; SendOrPostCallback,对象&gt;&GT;(); 公共覆盖无效发送(SendOrPostCallback D,对象状态)
{
抛出新NotSupportedException异常(我们不能送我们到同一个线程);
} 公共覆盖无效后(SendOrPostCallback D,对象状态)
{
锁定(项目)
{
items.Enqueue(Tuple.Create(D,状态));
}
workItemsWaiting.Set();
} 公共无效EndMessageLoop()
{
帖子(_ =&GT;完成= TRUE,NULL);
} 公共无效BeginMessageLoop()
{
而(!完成)
{
元组LT; SendOrPostCallback,对象&gt;任务= NULL;
锁定(项目)
{
如果(items.Count大于0)
{
任务= items.Dequeue();
}
}
如果(任务!= NULL)
{
task.Item1(task.Item2);
如果(的InnerException!= NULL)//该方法引发了exeption
{
抛出新AggregateException(AsyncHelpers.Run方法引发了异常。的InnerException);
}
}
其他
{
workItemsWaiting.WaitOne();
}
}
} 公众覆盖SynchronizationContext的CreateCopy()
{
返回此;
}
}
}I'm learning about async/await, and ran into a situation where I need to call an async method synchronously. How can I do that?
Async method:
public async Task<Customers> GetCustomers() { return await Service.GetCustomersAsync(); }
Normal usage:
public async void GetCustomers() { customerList = await GetCustomers(); }
I've tried using the following:
Task<Customer> task = GetCustomers(); task.Wait() Task<Customer> task = GetCustomers(); task.RunSynchronously(); Task<Customer> task = GetCustomers(); while(task.Status != TaskStatus.RanToCompletion)
I also tried a suggestion from here, however it doesn't work when the dispatcher is in a suspended state.
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(); }
Here is the exception and stack trace from calling
RunSynchronously
:System.InvalidOperationException
Message: RunSynchronously may not be called on a task unbound to a delegate.
InnerException: null
Source: 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()
解决方案Here's a workaround I found that works for all cases (including suspended dispatchers). It's not my code and I'm still working to fully understand it, but it does work.
It can be called using:
customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());
Code is from 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; } } }
这篇关于我将如何运行的异步任务&LT; T&GT;方法同步?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!