如何同步运行异步任务<T>方法?

我正在学习async/wait,并遇到需要同步调用async方法的情况。我该怎么做?

异步方法:

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的异常和堆栈跟踪:

系统。无效的操作异常

消息:在未绑定到委托的任务上可能不会调用RunSyn的方法。

内部异常: null#代码类型

来源:内存

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()
506955 次浏览

为什么不创建一个类似的调用:

Service.GetCustomers();

这不是async。

在你的代码中,你的第一个等待任务要执行,但你还没有启动它,所以它会无限期等待。试试这个:

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检查了你描述的异常发生在m_actionnull的时候。这有点奇怪,但我不是异步CTP的专家。正如我所说,你应该反编译你的代码,看看Task是如何被实例化的,为什么它的m_actionnull

这是我发现的适用于所有情况(包括挂起的调度程序)的变通方法。这不是我的代码,我仍在努力完全理解它,但它确实有效。

它可以被称为使用:

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;
}
}
}

如果我没看错你的问题——想要同步调用异步方法的代码正在挂起的调度程序线程上执行。你想同步那个线程,直到异步方法完成。

C#5中的异步方法通过有效地将方法切成碎片并返回一个Task来提供支持,该Task可以跟踪整个沙邦的整体完成情况。然而,切碎的方法如何执行取决于传递给await运算符的表达式的类型。

大多数时候,你将在Task类型的表达式上使用await。任务对await模式的实现是“聪明的”,因为它遵从SynchronizationContext,这基本上导致以下情况发生:

  1. 如果进入await的线程位于Dispatcher或WinForms消息循环线程上,则确保异步方法的块作为消息队列处理的一部分发生。
  2. 如果进入await的线程位于线程池线程上,则异步方法的剩余块发生在线程池的任何位置。

这就是为什么你可能会遇到问题——async方法实现试图在Dispatcher上运行其余部分——即使它被挂起了。

……备份!……

我不得不问一个问题,为什么你是否试图在异步方法上同步阻止?这样做会破坏为什么要异步调用该方法的目的。一般来说,当你开始在Dispatcher或UI方法上使用await时,你会希望将整个UI流变为异步。例如,如果你的调用堆栈类似于以下内容:

  1. [顶部]WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing()-WPFWinForms代码
  6. 【消息循环】-WPFWinForms消息循环

然后,一旦代码被转换为使用async,您通常会得到

  1. [顶部]WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing()-WPFWinForms代码
  6. 【消息循环】-WPFWinForms消息循环

真正的答案

上面的AsyncHelpers类实际上可以工作,因为它的行为类似于嵌套消息循环,但它将自己的并行机制安装到Dispatcher,而不是尝试在Dispatcher本身上执行。这是解决您问题的一种解决方法。

另一种解决方法是在线程池线程上执行你的async方法,然后等待它完成。这样做很容易-你可以使用以下代码段来做到这一点:

var customerList = TaskEx.RunEx(GetCustomers).Result;

最终的API将是Task. Run(…),但对于CTP,您需要Ex后缀(解释在这里)。

只是一个小注意-这种方法:

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的评论)

请注意这个答案是三年前的。我写它主要是基于. 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();

请看: 任务等待任务结束任务。同步运行


. Net 4.0

使用这个:

var x = (IAsyncResult)task;
task.Start();


x.AsyncWaitHandle.WaitOne();

…或者这个:

task.Start();
task.Wait();

关于wp8:

包装它:

Task GetCustomersSynchronously()
{
Task t = new Task(async () =>
{
myCustomers = await GetCustomers();
}
t.RunSynchronously();
}

叫它:

GetCustomersSynchronously();

在线程池上运行任务要简单得多,而不是试图欺骗调度程序同步运行它。这样你就可以确保它不会死锁。由于上下文切换,性能受到影响。

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;
    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");

这很简单,很容易,我没有任何问题。

这对我很管用

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;
}
}

此答案是为任何使用WPF for. NET 4.5的人设计的。

在不可避免的情况下,例如MVVM属性或对其他不使用async/wait的API的依赖,此类可以充当async/wait世界和非async/wait世界之间的粘合剂。

/// <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. Idty. 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 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);
}
}

很惊讶没有人提到这一点:

public Task<int> BlahAsync()
{
// ...
}


int result = BlahAsync().GetAwaiter().GetResult();

不像这里的其他一些方法那么漂亮,但它有以下好处:

  • 它不会吞下异常(如Wait
  • 它不会包装AggregateException(如Result)中抛出的任何异常
  • 适用于TaskTask<T>你自己试试吧!

此外,由于GetAwaiter是鸭子类型的,这应该适用于从异步方法(如ConfiguredAwaitableYieldAwaitable)返回的任何对象,而不仅仅是任务。


编辑:请注意,这种方法(或使用.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()

我认为下面的辅助方法也可以解决这个问题。

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);

或者你可以选择:

customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;

为此要编译,请确保引用扩展程序集:

System.Net.Http.Formatting

我正在学习async/wait,并遇到需要同步调用async方法的情况。我该怎么做?

最好的答案是你不,细节取决于“情况”是什么。

它是属性getter/setter吗?在大多数情况下,拥有异步方法比“异步属性”更好。(有关详细信息,请参阅我关于异步属性的博客文章)。

这是一个MVVM应用程序并且您想进行异步数据绑定吗?然后使用像我的NotifyTask这样的东西,如我的MSDN关于异步数据绑定的文章中所述。

它是构造函数吗?那么您可能需要考虑异步工厂方法。(有关更多信息,请参阅我的关于异步构造函数的博客文章)。

几乎总是有比异步同步更好的答案。

如果这不适合你的情况(你在这里问了一个问题描述情况就知道了这一点),那么我建议只使用同步代码。一路异步是最好的;一路同步是次好的。不推荐同步异步。

然而,有少数情况下sync-over-async是必要的。具体来说,你受到调用代码的约束,所以你要同步(并且绝对没有办法重新思考或重新构建你的代码以允许异步),要调用异步代码。这是非常罕见的情况,但它确实不时出现。

在这种情况下,您需要使用我在Brownfieldasync开发上的文章中描述的黑客之一,具体而言:

  • 阻塞(例如,GetAwaiter().GetResult())。请注意这会导致死锁(正如我在博客上描述的那样)。
  • 在线程池线程上运行代码(例如Task.Run(..).GetAwaiter().GetResult())。请注意,这仅在异步代码可以在线程池线程上运行时才有效(即不依赖于UI或ASP.NET上下文)。
  • 嵌套消息循环。请注意,只有当异步代码仅假定单线程上下文,而不是特定上下文类型(许多UI和ASP.NET代码需要特定上下文)时,这才有效。

嵌套消息循环是所有黑客中最危险的,因为它会导致重入。重新进入是非常棘手的原因,并且(IMO)是Windows上大多数应用程序错误的原因。特别是,如果你在UI线程上并且阻塞了工作队列(等待异步工作完成),那么CLR实际上会为你做一些消息泵送-它实际上会处理一些Win32消息从您的代码中。哦,你不知道哪些消息-当chrisbrumme他说:“如果能确切地知道什么会被抽出,那不是很好吗?不幸的是,抽出是一种超越凡人理解的黑色艺术。”时,那么我们真的没有希望知道。

所以,当你在UI线程上这样阻塞时,你就是在自找麻烦。cbrumme引用同一篇文章中的另一句话:“不时地,公司内外的客户发现我们在STA[UI线程]的托管阻塞期间泵送消息。这是一个合理的担忧,因为他们知道很难编写面对可重入的健壮代码。”

是的。非常很难编写面对重入时健壮的代码。嵌套消息循环你编写面对重入时健壮的代码。这就是为什么这个问题的公认(和最赞成的)答案在实践中是极其危险

如果你完全没有其他选择——你不能重新设计你的代码,你不能将其重组为异步——你被不可更改的调用代码强迫同步——你不能将下游代码更改为同步——你不能阻止——你不能在单独的线程上运行异步代码——那么只有到那时应该考虑接受可重入性。

如果你发现自己在这个角落,我建议使用类似Dispatcher.PushFrame for WPF应用程序的东西,与Application.DoEvents循环用于WinForm应用程序,对于一般情况,我自己的AsyncContext.Run

使用下面的代码片段

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));

我找到的同步运行任务且不阻塞UI线程的最简单方法是使用RunSyn的(),如:

Task t = new Task(() =>
{
//.... YOUR CODE ....
});
t.RunSynchronously();

就我而言,我有一个事件,当某些事情发生时触发。我不知道它会发生多少次。所以,我在我的事件中使用上面的代码,所以每当它触发时,它都会创建一个任务。任务是同步执行的,对我来说效果很好。我只是感到惊讶的是,考虑到它有多简单,我花了这么长时间才发现这一点。通常,建议要复杂得多,容易出错。这是简单而干净的。

正如许多人在评论中所说,简单地调用.Result;.Wait()会有死锁的风险。由于我们大多数人喜欢单线程序,因此您可以将它们用于.Net 4.5<

通过async方法获取值:

var result = Task.Run(() => asyncGetValue()).Result;

同步调用async方法

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;

编辑

如果调用者在线程池线程中运行(或者调用者也在任务中),在某些情况下仍然可能导致死锁。

我发现Spin等待在这方面工作得很好。

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或.等待()。它还允许您指定超时,以便在任务从未完成的情况下不会永远卡住。

注意:我认为,如果操作是异步的,不建议更改操作的性质,最好的做法是按原样处理(一路异步)。通过这种方式,您可以获得其他好处,如并行处理/多线程等。

看到其他答案没有使用这种方法,我也想在这里发布:

var customers = GetCustomersAsync().GetAwaiter().GetResult();

我知道这是一个老问题,但我想分享我的解决方案,可能不是最好的,但确实有效:

//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
}));
});
}

从同步代码执行异步任务在传统的. NET中提出了一些相当大的挑战:

  • 除非有适当的同步上下文,否则异步任务的等待操作容易发生死锁
  • 同步和异步代码的取消模型可能不同且不兼容

我真的认为我们应该在. NET BCL中拥有这种互操作性功能。同时,您可以使用Gapotchenko.FX.Threading NuGet包中的TaskBridge类。它提供了同步和异步代码执行模型之间的无缝互操作性:

using Gapotchenko.FX.Threading.Tasks;
using System;
using System.Threading.Tasks;


class Program
{
static void Main()
{
TaskBridge.Execute(RunAsync);
}


static async Task RunAsync()
{
await Console.Out.WriteLineAsync("Hello, Async World!");
}
}

带有TaskBridge类的NuGet包可用于这里


取消型号

TaskBridge提供不同取消模型之间的自动互操作性。

让我们从可以被Thread.Abort()方法中止的同步线程调用一个可取消的异步方法:

void SyncMethod() // can be canceled by Thread.Abort()
{
// Executes an async task that is gracefully canceled via cancellation
// token when current thread is being aborted or interrupted.
TaskBridge.Execute(DoJobAsync); // <-- TaskBridge DOES THE MAGIC
}


async Task DoJobAsync(CancellationToken ct)
{
…
// Gracefully handles cancellation opportunities.
ct.ThrowIfCancellationRequested();
…
}

现在,让我们看一下相反的场景,其中可取消的异步任务调用可中止的同步代码:

async Task DoJobAsync(CancellationToken ct) // can be canceled by a specified cancellation token
{
// Executes a synchronous method that is thread-aborted when
// a specified cancellation token is being canceled.
await TaskBridge.ExecuteAsync(SyncMethod, ct); // <-- TaskBridge DOES THE MAGIC
}


void SyncMethod()
{
…
}

上面的代码演示了两个执行模型之间完全互操作性的简单单行代码。

备注:从. NET Core开始的. NET的较新版本不支持Thread.Abort()。这没什么大不了的,因为您可以将取消令牌传递给可取消的方法。