async/await
C# で使用する場合、メソッドから戻り値が送信されない場合は、使用async void
する必要があります。Task
理にかなっています。奇妙なのは、週の初めに私が書いたいくつかのメソッドの単体テストを書いていて、NUnit がテストをいずれかまたは戻り値としてasync
マークすることを提案していることに気づいたことです。私はそれを試してみましたが、確かにうまくいきました。nunit フレームワークはどのようにしてメソッドを実行し、すべての非同期操作が完了するのを待つことができるのでしょうか? Task が返された場合は、そのタスクを待ってから必要な処理を実行できますが、void が返された場合はどうすればそれを実行できるでしょうか?async
void
Task
それで、ソースコードをクラックして開いて見つけました。小さなサンプルでそれを再現することはできますが、彼らが何をしているのかまったく理解できません。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);
}
}
}
上記のコードを実行すると、「Press any key to continue」メッセージの前に「Done Sleeping」と「Done awaiting」メッセージが表示されることに気付くでしょう。これは、非同期メソッドが何らかの形で待機していることを意味します。
私の質問は、誰かがここで何が起こっているのかを説明してもらえますか? 正確には何ですかSynchronizationContext
(あるスレッドから別のスレッドに作業を投稿するために使用されていることは知っています)が、すべての作業が完了するのを待つ方法についてはまだ混乱しています. 前もって感謝します!!