In my current project, I have a piece of code that, after simplifying it down to where I'm having issues, looks something like this:
private async Task RunAsync(CancellationToken cancel)
{
bool finished = false;
while (!cancel.IsCancellationRequested && !finished)
finished = await FakeTask();
}
private Task<bool> FakeTask()
{
return Task.FromResult(false);
}
If I use this code without awaiting, I end up blocking anyway:
// example 1
var task = RunAsync(cancel); // Code blocks here...
... // Other code that could run while RunAsync is doing its thing, but is forced to wait
await task;
// example 2
var task = RunAsync(cancelSource.Token); // Code blocks here...
cancelSource.Cancel(); // Never called
In the actual project, I'm not actually using FakeTask, and there usually will be some Task.Delay I'm awaiting in there, so the code most of the time doesn't actually block, or only for a limited amount of iterations.
In unit testing, however, I'm using a mock object that does pretty much do what FakeTask does, so when I want to see if RunAsync responds to its CancellationToken getting cancelled the way I expect it to, I'm stuck.
I have found I can fix this issue by adding for example await Task.Delay(1)
at the top of RunAsync, to force it to truly run asynchronous, but this feels a bit hacky. Are there better alternatives?
答案 0 :(得分:4)
您对await
所做的事情有不正确的心理印象。 await
的含义是:
在您的程序中,等待的“伪造”总是完整的,因此协程永远不会挂起。
还有更好的选择吗?
如果控制流逻辑要求您暂停协程,则使用Task.Yield
。
答案 1 :(得分:3)
Task.FromResult
actually runs synchronously, as would await Task.Delay(0)
. If you want to actually simulate asynchronous code, call Task.Yield()
. That creates an awaitable task that asynchronously yields back to the current context when awaited.
答案 2 :(得分:0)
As @SLaks said, your code will run synchronously. One thing is running async code, and another thing is running parallel code.
If you need to run your code in parallel you can use Task.Run
.
class Program
{
static async Task Main(string[] args)
{
var tcs = new CancellationTokenSource();
var task = Task.Run(() => RunAsync("1", tcs.Token));
var task2 = Task.Run(() => RunAsync("2", tcs.Token));
await Task.Delay(1000);
tcs.Cancel();
Console.ReadLine();
}
private static async Task RunAsync(string source, CancellationToken cancel)
{
bool finished = false;
while (!cancel.IsCancellationRequested && !finished)
finished = await FakeTask(source);
}
private static Task<bool> FakeTask(string source)
{
Console.WriteLine(source);
return Task.FromResult(false);
}
}
答案 3 :(得分:0)
C#的异步方法同步执行,直到必须等待结果为止。
在您的示例中,方法不必等待结果,因此循环会永远运行并因此阻塞调用方。
插入一个等待Task.Yield()以模拟一些实际的异步工作应该会有所帮助。