在C#中使用async/await
时,一般的规则是避免async void
,因为这几乎是一个错误,如果方法没有发送返回值,则应该使用task
。有道理。奇怪的是,在本周早些时候,我为我编写的一些异步
方法编写了一些单元测试,并注意到NUnit建议将异步
测试标记为void
或返回task
。然后我试了一下,果然奏效了。这似乎真的很奇怪,因为nunit框架如何能够运行该方法并等待所有异步操作完成?如果它返回Task,它可以等待任务,然后做它需要做的事情,但是如果它返回void,它如何完成它呢?
所以我破解了源代码并找到了它。我可以在一个小样本中复制它,但我根本无法理解他们在做什么。我想我对SynchronizationContext及其工作原理了解不够。代码如下:
class Program
{
static void Main(string[] args)
{
RunVoidAsyncAndWait();
Console.WriteLine("Press any key to continue. . .");
Console.ReadKey(true);
}
private static void RunVoidAsyncAndWait()
{
var previousContext = SynchronizationContext.Current;
var currentContext = new AsyncSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(currentContext);
try
{
var myClass = new MyClass();
var method = myClass.GetType().GetMethod("AsyncMethod");
var result = method.Invoke(myClass, null);
currentContext.WaitForPendingOperationsToComplete();
}
finally
{
SynchronizationContext.SetSynchronizationContext(previousContext);
}
}
}
public class MyClass
{
public async void AsyncMethod()
{
var t = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Done sleeping!");
});
await t;
Console.WriteLine("Done awaiting");
}
}
public class AsyncSynchronizationContext : SynchronizationContext
{
private int _operationCount;
private readonly AsyncOperationQueue _operations = new AsyncOperationQueue();
public override void Post(SendOrPostCallback d, object state)
{
_operations.Enqueue(new AsyncOperation(d, state));
}
public override void OperationStarted()
{
Interlocked.Increment(ref _operationCount);
base.OperationStarted();
}
public override void OperationCompleted()
{
if (Interlocked.Decrement(ref _operationCount) == 0)
_operations.MarkAsComplete();
base.OperationCompleted();
}
public void WaitForPendingOperationsToComplete()
{
_operations.InvokeAll();
}
private class AsyncOperationQueue
{
private bool _run = true;
private readonly Queue _operations = Queue.Synchronized(new Queue());
private readonly AutoResetEvent _operationsAvailable = new AutoResetEvent(false);
public void Enqueue(AsyncOperation asyncOperation)
{
_operations.Enqueue(asyncOperation);
_operationsAvailable.Set();
}
public void MarkAsComplete()
{
_run = false;
_operationsAvailable.Set();
}
public void InvokeAll()
{
while (_run)
{
InvokePendingOperations();
_operationsAvailable.WaitOne();
}
InvokePendingOperations();
}
private void InvokePendingOperations()
{
while (_operations.Count > 0)
{
AsyncOperation operation = (AsyncOperation)_operations.Dequeue();
operation.Invoke();
}
}
}
private class AsyncOperation
{
private readonly SendOrPostCallback _action;
private readonly object _state;
public AsyncOperation(SendOrPostCallback action, object state)
{
_action = action;
_state = state;
}
public void Invoke()
{
_action(_state);
}
}
}
在运行上面的代码时,您会注意到Done sleep和Done awaiting消息出现在按任意键继续消息之前,这意味着异步方法正在以某种方式等待。
我的问题是,有人能解释一下这里发生了什么吗?synchronizationcontext
到底是什么(我知道它用于将工作从一个线程发布到另一个线程),但我仍然不知道如何等待所有工作完成。提前感谢!!
SynchronizationContext
允许将工作发送到由另一个线程(或线程池)处理的队列--通常UI框架的消息循环用于此。Async
/await
功能在内部使用当前同步上下文,在等待的任务完成后返回到正确的线程。
AsyncSynchronizationContext
类实现自己的消息循环。发送到此上下文的工作将被添加到队列中。当程序调用WaitforPendingOperationStoComplete();
时,该方法通过从队列中获取工作并执行它来运行消息循环。如果在console.writeline(“done awaiting”);
上设置断点,您将看到它在WaitforPendingOperationStoComplete()
方法的主线程上运行。
此外,Async
/await
功能调用operationstarted()
/operationcompleted()
方法来通知SynchronizationContext
方法何时开始或结束执行。
AsyncSynchronizationContext
使用这些通知来统计正在运行但尚未完成的Async
方法的数量。当这个计数达到零时,WaitForPendingOperationStoComplete()
方法停止运行消息循环,控制流返回到调用方。
若要在调试器中查看此进程,请在同步上下文的post
、operationstarted
和operationcompleted
方法中设置断点。然后逐步执行AsyncMethod
调用:
AsyncMethod
时。NET首先调用OperationStarted()
_operationcount
设置为1。我正在编写一个WinForms应用程序,它将数据传输到USB HID类设备。我的应用程序使用了优秀的通用HID库V6.0,可以在这里找到。简单来说,当我需要向设备写入数据时,这是被调用的代码: 当我的代码退出while循环时,我需要从设备中读取一些数据。但是,设备无法立即响应,因此我需要等待此呼叫返回后再继续。由于当前存在,RequestToGetInputReport()声明如下: GetInp
我通读了Dart/flatter中的Async/Await/then,试图理解为什么aysnc函数中的Await不会等到完成后再继续。在我的UI中,有一个按钮调用一个异步方法来返回一个位置,该位置总是返回null,并且不等待函数完成。 该函数将调用推送到一个新的UI页面,该页面选择一个位置,并应返回一个结果。如何使该函数等待结果?我不是在使用异步吗?
问题内容: 我知道这个问题以前曾被问过,但是所有解决方案都不适合我。 我有一个将参数发送到API的函数,并以列表的形式返回数据。我有一个UITableView设置为使用该列表,但是它在列表分配给变量之前运行。 码: 如果不立即将其作为重复投票,我将不胜感激,这是我尝试的方法。 派遣组 信号量计时 运行变量 其中包括= self和= self 。 编辑:要求提取项目, 问题答案: 您不能-也不应该-
问题内容: 嗨,我的脚本中有2个Ajax调用,我需要它们运行asnyc以节省时间,但是我需要第二个才能等待第一个完成。 有什么想法吗?谢谢 问题答案: 如果使用jQuery 1.5+,则可以使用jQuery 完成。诸如此类的东西(缩短了ajax的简洁性,只需像上面那样传递对象) 您不知道它们将以什么顺序返回,因此,如果您手动滚动此请求,则需要检查另一个请求的状态并等待它返回。
我试图为从服务调用异步函数的函数编写测试,但我一辈子都不知道如何让Jasmine在执行expect函数之前等待异步操作完成。 我试图使用Jasmine的“完成”功能,但我不知道如何实现它。 在本例中,只要 调用时,它立即跳转到expect并失败,因为异步操作尚未完成。
在实际应用中,我对 C# 中的异步和 await 方法进行了说明。请考虑以程,这是工作代码的简单版本: 助手.cs 功能控制器.cs processing.js 现在,问题是第三方服务的所需时间更少(例如:2秒),而所需的时间更多(例如:100秒)。进行两个ajax调用并使用异步和等待是为了呈现自我。屏幕上显示数据1,不要等到自己。Data2已被检索。不幸的是,这种情况没有发生,我必须等待100秒