C# 如何将LongRunning标志专门传递给Task.Run()?

C# 如何将LongRunning标志专门传递给Task.Run()?,c#,.net,asynchronous,task,C#,.net,Asynchronous,Task,我需要一种方法将异步任务设置为长时间运行,而不使用task.Factory.StartNew(…),而是使用task.Run(…)或类似的东西 背景: 我有一个持续循环的任务,直到它被外部取消,我想设置为“长时间运行”(即给它一个专用线程)。这可以通过以下代码实现: var cts = new CancellationTokenSource(); Task t = Task.Factory.StartNew( async () => { while (true) { cts.Tok

我需要一种方法将异步任务设置为长时间运行,而不使用task.Factory.StartNew(…),而是使用task.Run(…)或类似的东西

背景:

我有一个持续循环的任务,直到它被外部取消,我想设置为“长时间运行”(即给它一个专用线程)。这可以通过以下代码实现:

var cts = new CancellationTokenSource();
Task t = Task.Factory.StartNew(
async () => {
while (true)
{
    cts.Token.ThrowIfCancellationRequested();
    try
    {
        "Running...".Dump();
        await Task.Delay(500, cts.Token);
    }
    catch (TaskCanceledException ex) { }
} }, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
问题在于Task.Factory.StartNew(…)不返回传入的活动异步任务,而是返回一个“运行操作的任务”,该任务在功能上始终具有“RanToCompletion”的taskStatus。由于我的代码需要能够跟踪任务的状态,以查看任务何时变为“已取消”(或“出现故障”),因此我需要使用以下内容:

var cts = new CancellationTokenSource();
Task t = Task.Run(
async () => {
while (true)
{
    cts.Token.ThrowIfCancellationRequested();
    try
    {
        "Running...".Dump();
        await Task.Delay(500, cts.Token);
    }
    catch (TaskCanceledException ex) { }
} }, cts.Token);

根据需要,Task.Run(…)返回异步进程本身,允许我获取“取消”或“故障”的实际状态。但是,我无法将任务指定为长时间运行。因此,任何人都知道如何最好地运行异步任务,同时存储活动任务本身(具有所需的taskStatus)并将任务设置为长时间运行?

在专用线程上,没有什么可以让人屈服的。不要使用
async
wait
,使用同步调用

提供两种无需等待即可取消睡眠的方法:

Task.Delay(500, cts.Token).Wait(); // requires .NET 4.5

cts.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(500)); // valid in .NET 4.0 and later

如果部分工作确实使用了并行性,则可以启动并行任务,将其保存到数组中,然后在
任务[]
上使用
任务.WaitAny
。仍然不能在主线程过程中使用
wait

对从
task.Factory.StartNew返回的任务调用
Unwrap
,这将返回具有正确状态的内部任务

var cts = new CancellationTokenSource();
Task t = Task.Factory.StartNew(
async () => {
while (true)
{
    cts.Token.ThrowIfCancellationRequested();
    try
    {
        "Running...".Dump();
        await Task.Delay(500, cts.Token);
    }
    catch (TaskCanceledException ex) { }
} }, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).Unwrap();
我有一个任务不断循环,直到它被外部取消,我想设置为“长时间运行”(即给它一个专用线程)。。。有人知道如何最好地运行异步任务,同时存储活动任务本身(具有所需的任务状态)并将任务设置为长时间运行吗

这有一些问题。首先,“长时间运行”并不一定意味着一个专用线程——它只是意味着你给TPL一个提示,任务是长时间运行的。在当前(4.5)实现中,您将获得一个专用线程;但这并不能保证,将来可能会改变

所以,如果你需要一个专用线程,你只需要创建一个

另一个问题是“异步任务”的概念。在线程池上运行的
async
代码的实际情况是,当异步操作(即
Task.Delay
)正在进行时,线程返回到线程池。然后,当异步操作完成时,从线程池中提取一个线程来恢复
async
方法。在一般情况下,这比专门保留一个线程来完成该任务更有效

因此,当
async
任务在线程池上运行时,专用线程就没有意义了


关于解决办法:

如果您确实需要一个专用线程来运行
async
代码,我建议您使用:

但是,几乎可以肯定的是,您不需要专门的线程。如果您的代码可以在线程池上执行,那么它可能应该执行;对于线程池上运行的
async
方法,专用线程没有意义。更具体地说,long-running标志对于线程池上运行的
async
方法没有意义

换句话说,对于
async
lambda,线程池实际执行的(并视为任务)只是
await
语句之间lambda的一部分。由于这些部件不是长时间运行的,因此不需要长时间运行标志。您的解决方案变成:

Task t = Task.Run(async () =>
{
  while (true)
  {
    cts.Token.ThrowIfCancellationRequested(); // not long-running
    try
    {
      "Running...".Dump(); // not long-running
      await Task.Delay(500, cts.Token); // not executed by the thread pool
    }
    catch (TaskCanceledException ex) { }
  }
});

这是不必要的,Task.Run就足够了,因为如果任何任务运行时间超过0.5秒,任务计划程序会将其设置为LongRunning

看看这里为什么。

您需要指定自定义任务CreationOptions。让我们考虑一下每一个 选项。AttachedToParent不应用于异步任务,因此 出局了。DenyChildatach应始终与异步任务一起使用 (提示:如果您还不知道,那么StartNew不是工具 你需要)。DenyChildatach由Task.Run传递。隐藏计划者可能 在一些非常模糊的调度场景中非常有用,但通常 异步任务应避免使用。那只剩下长跑和长跑 PreferFairity,这两个优化提示都应该 在应用程序分析之后指定。我经常看到龙润被滥用 特别地。在绝大多数情况下,线程池将 在0.5秒内调整到任何长时间运行的任务-无需 长跑旗。很可能,你并不真的需要它


我认为应该考虑的不是线程运行多长时间,而是它实际工作的时间。 在您的示例中,工作时间很短,他们等待任务。延迟(…)
。 如果您的项目中确实存在这种情况,您可能不应该为此任务使用专用线程,而让它在常规线程池中运行。每次在IO操作或在
Task.Delay()
上调用
wait
时,您都会释放线程以供其他任务使用

只有当您要从线程池中减少线程时,才应该使用
LongRunning
,并且决不归还线程,或者只在一小部分时间内归还线程。在这种情况下(工作很长,
Task.Delay(…)
比较短),为作业使用专用线程是一个合理的解决方案。 另一方面,如果您的线程大部分时间都在工作,那么它将消耗系统资源(CPU时间),并且如果它持有线程池中的线程,可能并不重要,因为它会阻止其他工作的发生

结论?只需使用
Task.Run()
(w
Task t = Task.Run(async () =>
{
  while (true)
  {
    cts.Token.ThrowIfCancellationRequested(); // not long-running
    try
    {
      "Running...".Dump(); // not long-running
      await Task.Delay(500, cts.Token); // not executed by the thread pool
    }
    catch (TaskCanceledException ex) { }
  }
});
var cts = new CancellationTokenSource();
Task t = DoWork();
async Task DoWork()
{
    while (true)
    {
        cts.Token.ThrowIfCancellationRequested();
        try
        {
            "Running...".Dump();
            await Task.Delay(500, cts.Token);
        }
        catch (TaskCanceledException) { }
    }
}