在代码调用Task.Result
时,它已经在等待,所以这里的异步模式仍然成立吗?
class Program
{
static async Task Main(string[] args)
{
var addNumbersTask = AddNumbers(10, 20);
var result = AwaitForResult(addNumbersTask).Result;
Console.WriteLine(result);
}
static async Task<int> AddNumbers(int a, int b)
{
await Task.Delay(250);
return a + b;
}
static async Task<int> AwaitForResult(Task<int> task)
{
await task;
return task.Result;
}
}
如果您感兴趣的话,请尝试:尝试为需要处理异步调用的代理类发出IL代码,但是我不想在IL中生成异步状态机。因此,我认为可以将实际的“等待”部分委派给IL之外的帮助者。另外,我知道那里有代理类型,但我内无望的工程师想自己写。
编辑:更新示例。
interface IService
{
Task<int> AddAsync(int a, int b);
}
class Service : IService
{
public async Task<int> AddAsync(int a, int b)
{
await Task.Delay(250); // Some web service call...
return a + b;
}
}
// This class 100% generated via reflection emit
class Proxy : IService
{
private readonly IService _actual;
public Proxy(IService actual) => _actual = actual;
public Task<int> AddAsync(int a, int b)
{
return Awaiter.Await(_actual.AddAsync(a, b));
}
}
static class Awaiter
{
public static async Task<int> Await(Task<int> task)
{
return await task;
}
}
class Program
{
static async Task Main(string[] args)
{
var proxy = new Proxy(new Service());
var result = await proxy.AddAsync(5, 5);
Console.WriteLine($"Result is {result}", result);
}
}
答案 0 :(得分:1)
这里的异步模式仍然成立吗?
不。在任务上使用await
并没有什么神奇的。相反,请考虑任务是否完成。调用task.Result
将阻塞调用线程,直到task
完成。 await task
会异步等待,直到task
完成。
因此在此代码中,Result
将不会阻止:
static async Task<int> AwaitForResult(Task<int> task)
{
// Task may not yet be complete here.
await task;
// At this point, task is complete.
// Since task is complete, Result does not block.
return task.Result;
}
但这与这段代码完全不同:
var result = AwaitForResult(addNumbersTask).Result;
// Equivalent to:
var task = AwaitForResult(addNumbersTask);
var result = task.Result;
从AwaitForResult
返回的任务在这里可能未完成,因为它从未被await
处理。如果未完成,则Result
将阻止。
试图为需要处理异步调用的代理类发出IL代码,但是我不想在IL中生成异步状态机。因此,我认为可以将实际的“等待”部分委派给IL之外的帮助者。
您是否尝试过Roslyn API?我发现它们比IL发射要方便得多。
如果您的代理服务器只是一个传递对象,那么您可以直接返回内部任务:
// This class 100% generated via reflection emit
class Proxy : IService
{
private readonly IService _actual;
public Proxy(IService actual) => _actual = actual;
public Task<int> AddAsync(int a, int b) => _actual.AddAsync(a, b);
}
但是,如果您想添加很多实际的逻辑,那么我建议使用Roslyn为您生成async
状态机。
答案 1 :(得分:0)
如果您想避免屏蔽,则不会。
在您的控制台应用程序示例中,它几乎没有什么区别,但是如果您在不想阻止的地方这样做-例如UI事件处理程序-.Result
仍会阻止。
等到addNumbersTask
中有一个值时,该任务就会启动。然后,您立即将任务传递给AwaitForResult
,后者立即开始等待它,此时返回int
的 incomplete 任务将返回到main()
,然后在其上调用.Result
。现在,您将在250ms的大部分时间内一直阻塞该线程,直到该任务将其int
结果返回到.Result
中的main()
。
您在问题“等待中”的表达方式不等于“完成”。
答案 2 :(得分:0)
方法Awaiter.Await
基本没有意义。除了增加一些开销外,返回的任务在功能上与传入的任务相同。
简单地返回_actual.AddAsync(a, b)
的结果和等待它之间的唯一实际区别是,如果AddAsync
引发异常,如果方法为async
,它将返回错误任务而不是抛出。因此,如果无法执行该操作,或者如果Proxy.AddAsync
可以在这种情况下安全地抛出自身,则只需返回该值,而无需执行任何操作。如果您需要确保Proxy.AddAsync
从不抛出异常,而如果Proxy.AddAsync
抛出异常,则返回错误的任务,那么您只需要将方法包装在try/catch
中并返回一个catch块中的错误异常。您无需复制async
状态机的其余逻辑。
因此,假设您希望与服务方法的行为具有相同的异常语义(我怀疑这样做),则可以将这些方法与同步方法完全一样,也就是说,使用适当的方法来调用该方法。参数并返回结果。