omi*_*nug 3 .net c# multithreading asynchronous async-await
与Task.Wait()or 相反Task.Result,await' Task在C#5中阻止执行等待的线程处于休眠状态.相反,使用await关键字的方法需要async使得调用await只是使方法返回表示async方法执行的新任务.
但是当await'ed Task在async方法再次收到CPU时间之前完成时,await识别Task为已完成,因此该async方法将Task仅在稍后返回该对象.在某些情况下,这可能比可接受的晚,因为开发人员认为await'ing始终推迟其async方法中的后续语句可能是一个常见的错误.
错误async方法的结构可能如下所示:
async Task doSthAsync()
{
var a = await getSthAsync();
// perform a long operation
}
Run Code Online (Sandbox Code Playgroud)
然后有时doSthAsync()会Task在很长一段时间后返回.
我知道它应该像这样写:
async Task doSthAsync()
{
var a = await getSthAsync();
await Task.Run(() =>
{
// perform a long operation
};
}
Run Code Online (Sandbox Code Playgroud)
... 或者那个:
async Task doSthAsync()
{
var a = await getSthAsync();
await Task.Yield();
// perform a long operation
}
Run Code Online (Sandbox Code Playgroud)
但我没有找到最后两个模式漂亮,并希望防止错误发生.我正在开发一个提供的框架,getSthAsync第一个结构应该是通用的.所以getSthAsync应该返回一个Awaitable,它总是产生像YieldAwaitable返回的Task.Yield()那样.
不幸的是,任务并行库提供的大多数功能Task.WhenAll(IEnumerable<Task> tasks)只能在Tasks上运行,因此结果getSthAsync应该是a Task.
那么有可能返回一个Task总是收益的吗?
首先,异步方法的消费者不应该假设它会"屈服",因为它与异步无关.如果消费者需要确保卸载到另一个线程,他们应该使用它Task.Run来强制执行.
其次,我没有看到如何使用Task.Run或者Task.Yield有问题,因为它在异步方法中使用它返回a Task而不是a YieldAwaitable.
如果你想创建一个Task行为就像YieldAwaitable你可以Task.Yield在异步方法中使用:
async Task Yield()
{
await Task.Yield();
}
Run Code Online (Sandbox Code Playgroud)
编辑:
正如评论中提到的那样,这种竞争条件可能并不总是会产生.这种竞争状态是固有如何Task和TaskAwaiter实现.要避免这种情况,你可以创建自己的Task和TaskAwaiter:
public class YieldTask : Task
{
public YieldTask() : base(() => {})
{
Start(TaskScheduler.Default);
}
public new TaskAwaiterWrapper GetAwaiter() => new TaskAwaiterWrapper(base.GetAwaiter());
}
public struct TaskAwaiterWrapper : INotifyCompletion
{
private TaskAwaiter _taskAwaiter;
public TaskAwaiterWrapper(TaskAwaiter taskAwaiter)
{
_taskAwaiter = taskAwaiter;
}
public bool IsCompleted => false;
public void OnCompleted(Action continuation) => _taskAwaiter.OnCompleted(continuation);
public void GetResult() => _taskAwaiter.GetResult();
}
Run Code Online (Sandbox Code Playgroud)
这将创建一个始终产生的任务,因为IsCompleted 始终返回false.它可以像这样使用:
public static readonly YieldTask YieldTask = new YieldTask();
private static async Task MainAsync()
{
await YieldTask;
// something
}
Run Code Online (Sandbox Code Playgroud)
注意:我强烈反对任何人实际做这种事情.
| 归档时间: |
|
| 查看次数: |
206 次 |
| 最近记录: |