如何创建一个总是产生收益的任务? [英] How to create a Task which always yields?
问题描述
与 Task.Wait()
或 Task.Result
相比, await
在C#5中执行 Task
可以防止执行等待的线程处于休止状态。相反,使用 await
关键字的方法需要 async
,以便调用 await
只是使该方法返回一个新任务,该任务表示执行 async
方法。
In contrast to Task.Wait()
or Task.Result
, await
’ing a Task
in C# 5 prevents the thread which executes the wait from lying fallow. Instead, the method using the await
keyword needs to be async
so that the call of await
just makes the method to return a new task which represents the execution of the async
method.
但是,当等待
编辑了任务
时,会在 async $ c之前完成$ c>方法再次获得了CPU时间,
等待
将任务
识别为已完成,因此将 async
方法将仅在以后返回 Task
对象。在某些情况下,这可能会晚于可接受的时间,因为这可能是一个常见错误,即开发人员认为 await
总是会延迟其中的后续语句异步
方法。
But when the await
’ed Task
completes before the async
method has received CPU time again, the await
recognizes the Task
as finished and thus the async
method will return the Task
object only at a later time. In some cases this would be later than acceptable because it probably is a common mistake that a developer assumes the await
’ing always defers the subsequent statements in his async
method.
错误的异步
方法的结构如下所示:
The mistaken async
method’s structure could look like the following:
async Task doSthAsync()
{
var a = await getSthAsync();
// perform a long operation
}
然后有时 doSthAsync()
仅在很长一段时间后才会返回 Task
。
Then sometimes doSthAsync()
will return the Task
only after a long time.
我知道应该这样写:
async Task doSthAsync()
{
var a = await getSthAsync();
await Task.Run(() =>
{
// perform a long operation
};
}
...或那样:
async Task doSthAsync()
{
var a = await getSthAsync();
await Task.Yield();
// perform a long operation
}
但是我找不到最后两种模式,并且想要防止错误我正在开发一个提供 getSthAsync
的框架,并且第一个结构应该是通用的,因此 getSthAsync
应该返回一个Awaitable总是像 Task.Yield()
返回的 YieldAwaitable
一样。
But I do not find the last two patterns pretty and want to prevent the mistake to occur. I am developing a framework which provides getSthAsync
and the first structure shall be common. So getSthAsync
should return an Awaitable which always yields like the YieldAwaitable
returned by Task.Yield()
does.
不幸的是,任务并行库提供的大多数功能,例如 Task.WhenAll(IEnumerable< Task> task)
仅在 Task上运行
s,所以 getSthAsync
的结果应该是 Task
。
Unfortunately most features provided by the Task Parallel Library like Task.WhenAll(IEnumerable<Task> tasks)
only operate on Task
s so the result of getSthAsync
should be a Task
.
是吗可能返回总是产生收益的 Task
?
So is it possible to return a Task
which always yields?
推荐答案
首先,异步方法的使用者不应假定它将屈服,因为这与异步无关。如果使用者需要确保另一个线程可以卸载,则应使用 Task.Run
来强制执行。
First of all, the consumer of an async method shouldn't assume it will "yield" as that's nothing to do with it being async. If the consumer needs to make sure there's an offload to another thread they should use Task.Run
to enforce that.
第二,我不知道使用 Task.Run
或 Task.Yield
有什么问题,因为在异步方法内部使用,该方法返回 Task
而不是 YieldAwaitable
。
Second of all, I don't see how using Task.Run
, or Task.Yield
is problematic as it's used inside an async method which returns a Task
and not a YieldAwaitable
.
如果您要创建行为类似于 YieldAwaitable
的 Task
,则只需使用 Task.Yield
在异步方法中:
If you want to create a Task
that behaves like YieldAwaitable
you can just use Task.Yield
inside an async method:
async Task Yield()
{
await Task.Yield();
}
编辑:
正如评论中提到的那样,这是一种竞争条件,可能并不总是让步。此竞争条件与 Task
和 TaskAwaiter
的实现方式有关。为避免这种情况,您可以创建自己的 Task
和 TaskAwaiter
:
As was mentioned in the comments, this has a race condition where it may not always yield. This race condition is inherent with how Task
and TaskAwaiter
are implemented. To avoid that you can create your own Task
and 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();
}
这将创建一个始终产生的任务,因为已完成
总是返回false。可以这样使用:
This will create a task that always yields because IsCompleted
always returns false. It can be used like this:
public static readonly YieldTask YieldTask = new YieldTask();
private static async Task MainAsync()
{
await YieldTask;
// something
}
注意:我强烈劝阻任何人不要实际做这种事情。
Note: I highly discourage anyone from actually doing this kind of thing.
这篇关于如何创建一个总是产生收益的任务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!