c# - How would I run an async Task<T> method synchronously? -
i'm learning async/await, , ran situation need call async method synchronously. how can 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 following:
task<customer> task = getcustomers(); task.wait() task<customer> task = getcustomers(); task.runsynchronously(); task<customer> task = getcustomers(); while(task.status != taskstatus.rantocompletion)
i tried suggestion here, doesn't work when dispatcher in 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 exception , stack trace calling runsynchronously
:
system.invalidoperationexception
message: runsynchronously may not called on task unbound delegate.
innerexception: null
source: mscorlib
stacktrace:
@ system.threading.tasks.task.internalrunsynchronously(taskscheduler scheduler) @ system.threading.tasks.task.runsynchronously() @ myapplication.customcontrols.controls.mycustomcontrol.createavailablepanellist() in c:\documents , settings\...\myapplication.customcontrols\controls\mycustomcontrol.xaml.cs:line 638 @ myapplication.customcontrols.controls.mycustomcontrol.get_availablepanels() in c:\documents , settings\...\myapplication.customcontrols\controls\mycustomcontrol.xaml.cs:line 233 @ myapplication.customcontrols.controls.mycustomcontrol.<createopenpanellist>b__36(desktoppanel panel) in c:\documents , settings\...\myapplication.customcontrols\controls\mycustomcontrol.xaml.cs:line 597 @ system.collections.generic.list`1.foreach(action`1 action) @ myapplication.customcontrols.controls.mycustomcontrol.<createopenpanellist>d__3b.movenext() in c:\documents , settings\...\myapplication.customcontrols\controls\mycustomcontrol.xaml.cs:line 625 @ system.runtime.compilerservices.taskawaiter.<>c__displayclass7.<trysetcontinuationforawait>b__1(object state) @ system.windows.threading.exceptionwrapper.internalrealcall(delegate callback, object args, int32 numargs) @ ms.internal.threading.exceptionfilterhelper.trycatchwhen(object source, delegate method, object args, int32 numargs, delegate catchhandler) @ system.windows.threading.dispatcheroperation.invokeimpl() @ system.windows.threading.dispatcheroperation.invokeinsecuritycontext(object state) @ system.threading.executioncontext.runtrycode(object userdata) @ system.runtime.compilerservices.runtimehelpers.executecodewithguaranteedcleanup(trycode code, cleanupcode backoutcode, object userdata) @ system.threading.executioncontext.runinternal(executioncontext executioncontext, contextcallback callback, object state) @ system.threading.executioncontext.run(executioncontext executioncontext, contextcallback callback, object state, boolean ignoresyncctx) @ system.threading.executioncontext.run(executioncontext executioncontext, contextcallback callback, object state) @ system.windows.threading.dispatcheroperation.invoke() @ system.windows.threading.dispatcher.processqueue() @ system.windows.threading.dispatcher.wndprochook(intptr hwnd, int32 msg, intptr wparam, intptr lparam, boolean& handled) @ ms.win32.hwndwrapper.wndproc(intptr hwnd, int32 msg, intptr wparam, intptr lparam, boolean& handled) @ ms.win32.hwndsubclass.dispatchercallbackoperation(object o) @ system.windows.threading.exceptionwrapper.internalrealcall(delegate callback, object args, int32 numargs) @ ms.internal.threading.exceptionfilterhelper.trycatchwhen(object source, delegate method, object args, int32 numargs, delegate catchhandler) @ system.windows.threading.dispatcher.invokeimpl(dispatcherpriority priority, timespan timeout, delegate method, object args, int32 numargs) @ ms.win32.hwndsubclass.subclasswndproc(intptr hwnd, int32 msg, intptr wparam, intptr lparam) @ ms.win32.unsafenativemethods.dispatchmessage(msg& msg) @ system.windows.threading.dispatcher.pushframeimpl(dispatcherframe frame) @ system.windows.threading.dispatcher.pushframe(dispatcherframe frame) @ system.windows.threading.dispatcher.run() @ system.windows.application.rundispatcher(object ignore) @ system.windows.application.runinternal(window window) @ system.windows.application.run(window window) @ system.windows.application.run() @ myapplication.app.main() in c:\documents , settings\...\myapplication\obj\debug\app.g.cs:line 50 @ system.appdomain._nexecuteassembly(runtimeassembly assembly, string[] args) @ system.appdomain.executeassembly(string assemblyfile, evidence assemblysecurity, string[] args) @ microsoft.visualstudio.hostingprocess.hostproc.runusersassembly() @ system.threading.threadhelper.threadstart_context(object state) @ system.threading.executioncontext.run(executioncontext executioncontext, contextcallback callback, object state, boolean ignoresyncctx) @ system.threading.executioncontext.run(executioncontext executioncontext, contextcallback callback, object state) @ system.threading.threadhelper.threadstart()
here's workaround found works cases (including suspended dispatchers). it's not code , i'm still working understand it, work.
it can called using:
customerlist = asynchelpers.runsync<list<customer>>(() => getcustomers());
code here
public static class asynchelpers { /// <summary> /// execute's async task<t> method has void return value synchronously /// </summary> /// <param name="task">task<t> method 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; } { synch.endmessageloop(); } }, null); synch.beginmessageloop(); synchronizationcontext.setsynchronizationcontext(oldcontext); } /// <summary> /// execute's async task<t> method has t return type synchronously /// </summary> /// <typeparam name="t">return type</typeparam> /// <param name="task">task<t> method 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; } { 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 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) // method threw exeption { throw new aggregateexception("asynchelpers.run method threw exception.", innerexception); } } else { workitemswaiting.waitone(); } } } public override synchronizationcontext createcopy() { return this; } } }
Comments
Post a Comment