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

时间:2022-04-19 02:23:18

I need a way to set an async task as long running without using Task.Factory.StartNew(...) and instead using Task.Run(...) or something similar.

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

Context:

I have Task that loops continuously until it is externally canceled that I would like to set as 'long running' (i.e. give it a dedicated thread). This can be achieved through the code below:

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

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);

The problem is that Task.Factory.StartNew(...) does not return the active async task that is passed in but rather a 'task of running the Action' which functionally always has taskStatus of 'RanToCompletion'. Since my code needs to be able to track the task's status to see when it becomes 'Canceled' (or 'Faulted') I need to use something like below:

问题是Task.Factory.StartNew(...)不返回传入的活动异步任务,而是返回“运行Action的任务”,其功能上始终具有taskTatus的'RanToCompletion'。由于我的代码需要能够跟踪任务的状态以查看它何时变为“已取消”(或“故障”),我需要使用如下所示的内容:

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(...), as desired, returns the async process itself allowing me to obtain actual statuses of 'Canceled' or 'Faulted'. I cannot specify the task as long running, however. So, anyone know how to best go about running an async task while both storing that active task itself (with desired taskStatus) and setting the task to long running?

Task.Run(...),根据需要,返回异步过程本身,允许我获得'已取消'或'故障'的实际状态。但是,我无法将任务指定为长时间运行。那么,任何人都知道如何最好地运行异步任务,同时存储该活动任务本身(使用所需的taskStatus)并将任务设置为长时间运行?

4 个解决方案

#1


6  

Call Unwrap on the task returned from Task.Factory.StartNew this will return the inner task, which has the correct status.

从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();

#2


20  

I have Task that loops continuously until it is externally canceled that I would like to set as 'long running' (i.e. give it a dedicated thread)... anyone know how to best go about running an async task while both storing that active task itself (with desired taskStatus) and setting the task to long running?

我有持续循环的任务,直到它被外部取消,我想设置为'长时间运行'(即给它一个专用线程)...任何人都知道如何最好地运行异步任务同时存储该活动任务本身(具有所需的taskStatus)并将任务设置为长时间运行?

There's a few problems with this. First, "long running" does not necessarily mean a dedicated thread - it just means that you're giving the TPL a hint that the task is long-running. In the current (4.5) implementation, you will get a dedicated thread; but that's not guaranteed and could change in the future.

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

So, if you need a dedicated thread, you'll have to just create one.

因此,如果您需要专用线程,则必须创建一个。

The other problem is the notion of an "asynchronous task". What actually happens with async code running on the thread pool is that the thread is returned to the thread pool while the asynchronous operation (i.e., Task.Delay) is in progress. Then, when the async op completes, a thread is taken from the thread pool to resume the async method. In the general case, this is more efficient than reserving a thread specifically to complete that task.

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

So, with async tasks running on the thread pool, dedicated threads don't really make sense.

因此,在线程池上运行异步任务时,专用线程确实没有意义。


Regarding solutions:

If you do need a dedicated thread to run your async code, I'd recommend using the AsyncContextThread from my AsyncEx library:

如果您确实需要专用线程来运行异步代码,我建议使用AsyncEx库中的AsyncContextThread:

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

However, you almost certainly don't need a dedicated thread. If your code can execute on the thread pool, then it probably should; and a dedicated thread doesn't make sense for async methods running on the thread pool. More specifically, the long-running flag doesn't make sense for async methods running on the thread pool.

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

Put another way, with an async lambda, what the thread pool actually executes (and sees as tasks) are just the parts of the lambda in-between the await statements. Since those parts aren't long-running, the long-running flag is not required. And your solution becomes:

换句话说,使用异步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) { }
  }
});

#3


2  

On a dedicated thread, there's nothing to yield to. Don't use async and await, use synchronous calls.

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

This question gives two ways to do a cancellable sleep without await:

这个问题提供了两种方法可以在不等待的情况下进行可取消的睡眠:

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

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

If part of your work does use parallelism, you can start parallel tasks, saving those into an array, and use Task.WaitAny on the Task[]. Still no use for await in the main thread procedure.

如果你的部分工作确实使用了并行性,你可以启动并行任务,将它们保存到数组中,并在Task []上使用Task.WaitAny。在主线程程序中仍然没有用于等待。

#4


0  

This is unnecessary and Task.Run will suffice as the Task Scheduler will set any task to LongRunning if it runs for more than 0.5 seconds.

这是不必要的,Task.Run就足够了,因为任务调度程序将任何任务设置为LongRunning,如果它运行超过0.5秒。

See here why. https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html

看到这里的原因。 https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html

You need to specify custom TaskCreationOptions. Let’s consider each of the options. AttachedToParent shouldn’t be used in async tasks, so that’s out. DenyChildAttach should always be used with async tasks (hint: if you didn’t already know that, then StartNew isn’t the tool you need). DenyChildAttach is passed by Task.Run. HideScheduler might be useful in some really obscure scheduling scenarios but in general should be avoided for async tasks. That only leaves LongRunning and PreferFairness, which are both optimization hints that should only be specified after application profiling. I often see LongRunning misused in particular. In the vast majority of situations, the threadpool will adjust to any long-running task in 0.5 seconds - without the LongRunning flag. Most likely, you don’t really need it.

您需要指定自定义TaskCreationOptions。让我们考虑每个选项。 AttachedToParent不应该用在异步任务中,所以这样就完成了。 DenyChildAttach应始终与异步任务一起使用(提示:如果您还不知道,那么StartNew不是您需要的工具)。 DenyChildAttach由Task.Run传递。 HideScheduler在某些非常模糊的调度方案中可能很有用,但对于异步任务通常应该避免使用。这只留下LongRunning和PreferFairness,这两个优化提示只应在应用程序分析后指定。我经常看到LongRunning被特别滥用。在绝大多数情况下,线程池将在0.5秒内调整为任何长时间运行的任务 - 没有LongRunning标志。最有可能的是,你真的不需要它。

#1


6  

Call Unwrap on the task returned from Task.Factory.StartNew this will return the inner task, which has the correct status.

从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();

#2


20  

I have Task that loops continuously until it is externally canceled that I would like to set as 'long running' (i.e. give it a dedicated thread)... anyone know how to best go about running an async task while both storing that active task itself (with desired taskStatus) and setting the task to long running?

我有持续循环的任务,直到它被外部取消,我想设置为'长时间运行'(即给它一个专用线程)...任何人都知道如何最好地运行异步任务同时存储该活动任务本身(具有所需的taskStatus)并将任务设置为长时间运行?

There's a few problems with this. First, "long running" does not necessarily mean a dedicated thread - it just means that you're giving the TPL a hint that the task is long-running. In the current (4.5) implementation, you will get a dedicated thread; but that's not guaranteed and could change in the future.

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

So, if you need a dedicated thread, you'll have to just create one.

因此,如果您需要专用线程,则必须创建一个。

The other problem is the notion of an "asynchronous task". What actually happens with async code running on the thread pool is that the thread is returned to the thread pool while the asynchronous operation (i.e., Task.Delay) is in progress. Then, when the async op completes, a thread is taken from the thread pool to resume the async method. In the general case, this is more efficient than reserving a thread specifically to complete that task.

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

So, with async tasks running on the thread pool, dedicated threads don't really make sense.

因此,在线程池上运行异步任务时,专用线程确实没有意义。


Regarding solutions:

If you do need a dedicated thread to run your async code, I'd recommend using the AsyncContextThread from my AsyncEx library:

如果您确实需要专用线程来运行异步代码,我建议使用AsyncEx库中的AsyncContextThread:

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

However, you almost certainly don't need a dedicated thread. If your code can execute on the thread pool, then it probably should; and a dedicated thread doesn't make sense for async methods running on the thread pool. More specifically, the long-running flag doesn't make sense for async methods running on the thread pool.

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

Put another way, with an async lambda, what the thread pool actually executes (and sees as tasks) are just the parts of the lambda in-between the await statements. Since those parts aren't long-running, the long-running flag is not required. And your solution becomes:

换句话说,使用异步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) { }
  }
});

#3


2  

On a dedicated thread, there's nothing to yield to. Don't use async and await, use synchronous calls.

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

This question gives two ways to do a cancellable sleep without await:

这个问题提供了两种方法可以在不等待的情况下进行可取消的睡眠:

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

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

If part of your work does use parallelism, you can start parallel tasks, saving those into an array, and use Task.WaitAny on the Task[]. Still no use for await in the main thread procedure.

如果你的部分工作确实使用了并行性,你可以启动并行任务,将它们保存到数组中,并在Task []上使用Task.WaitAny。在主线程程序中仍然没有用于等待。

#4


0  

This is unnecessary and Task.Run will suffice as the Task Scheduler will set any task to LongRunning if it runs for more than 0.5 seconds.

这是不必要的,Task.Run就足够了,因为任务调度程序将任何任务设置为LongRunning,如果它运行超过0.5秒。

See here why. https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html

看到这里的原因。 https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html

You need to specify custom TaskCreationOptions. Let’s consider each of the options. AttachedToParent shouldn’t be used in async tasks, so that’s out. DenyChildAttach should always be used with async tasks (hint: if you didn’t already know that, then StartNew isn’t the tool you need). DenyChildAttach is passed by Task.Run. HideScheduler might be useful in some really obscure scheduling scenarios but in general should be avoided for async tasks. That only leaves LongRunning and PreferFairness, which are both optimization hints that should only be specified after application profiling. I often see LongRunning misused in particular. In the vast majority of situations, the threadpool will adjust to any long-running task in 0.5 seconds - without the LongRunning flag. Most likely, you don’t really need it.

您需要指定自定义TaskCreationOptions。让我们考虑每个选项。 AttachedToParent不应该用在异步任务中,所以这样就完成了。 DenyChildAttach应始终与异步任务一起使用(提示:如果您还不知道,那么StartNew不是您需要的工具)。 DenyChildAttach由Task.Run传递。 HideScheduler在某些非常模糊的调度方案中可能很有用,但对于异步任务通常应该避免使用。这只留下LongRunning和PreferFairness,这两个优化提示只应在应用程序分析后指定。我经常看到LongRunning被特别滥用。在绝大多数情况下,线程池将在0.5秒内调整为任何长时间运行的任务 - 没有LongRunning标志。最有可能的是,你真的不需要它。