Rya*_*yan 19 .net c# asynchronous task
我需要一种方法来设置异步任务,因为长时间运行而不使用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);
Run Code Online (Sandbox Code Playgroud)
问题是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);
Run Code Online (Sandbox Code Playgroud)
Task.Run(...),根据需要,返回异步过程本身,允许我获得'已取消'或'故障'的实际状态.但是,我无法将任务指定为长时间运行.那么,任何人都知道如何最好地运行异步任务,同时存储该活动任务本身(使用所需的taskStatus)并将任务设置为长时间运行?
Ste*_*ary 23
我有持续循环的任务,直到它被外部取消,我想设置为'长时间运行'(即给它一个专用线程)...任何人都知道如何最好地运行异步任务同时存储该活动任务本身(具有所需的taskStatus)并将任务设置为长时间运行?
这有一些问题.首先,"长时间运行"并不一定意味着专用线程 - 它只是意味着您向TPL提供了任务长期运行的提示.在当前(4.5)实现中,您将获得专用线程; 但这不保证,将来可能会改变.
因此,如果您需要专用线程,则必须创建一个.
另一个问题是"异步任务"的概念.async在线程池上运行的代码实际发生的是线程在异步操作(即Task.Delay)正在进行时返回到线程池.然后,当异步操作完成时,从线程池中取出一个线程以恢复该async方法.在一般情况下,这比专门保留线程以完成该任务更有效.
因此,async在线程池上运行任务时,专用线程实际上没有意义.
关于解决方案
如果你确实需要一个专用的线程来运行你的async代码,我建议你使用AsyncContextThread我的AsyncEx库:
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) { }
}
});
}
Run Code Online (Sandbox Code Playgroud)
但是,您几乎肯定不需要专用线程.如果您的代码可以在线程池上执行,那么它可能应该; 并且专用线程async对线程池上运行的方法没有意义.更具体地说,长时间运行的标志对于async在线程池上运行的方法没有意义.
换句话说,使用asynclambda,线程池实际执行的内容(并视为任务)只是语句之间lambda 的部分await.由于这些部件没有长时间运行,因此不需要长时间运行的标志.你的解决方案变为:
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) { }
}
});
Run Code Online (Sandbox Code Playgroud)
调用Unwrap从此Task.Factory.StartNew返回的任务将返回具有正确状态的内部任务.
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();
Run Code Online (Sandbox Code Playgroud)
在专用线程上,没有什么可屈服的。不要使用asyncand await,使用同步调用。
这个问题提供了两种方法来进行可取消的睡眠,而无需await:
Task.Delay(500, cts.Token).Wait(); // requires .NET 4.5
cts.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(500)); // valid in .NET 4.0 and later
Run Code Online (Sandbox Code Playgroud)
如果您的部分工作确实使用了并行性,您可以启动并行任务,将它们保存到一个数组中,然后Task.WaitAny在Task[]. await在主线程程序中仍然没有用。
这是不必要的,Task.Run 就足够了,因为如果任务运行时间超过 0.5 秒,任务计划程序会将任何任务设置为 LongRunning。
看看这里为什么。 https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html
您需要指定自定义 TaskCreationOptions。让我们考虑每个选项。不应该在异步任务中使用 AttachedToParent,所以就这样了。DenyChildAttach 应始终与异步任务一起使用(提示:如果您还不知道,那么 StartNew 不是您需要的工具)。DenyChildAttach 由 Task.Run 传递。HideScheduler 在一些非常模糊的调度场景中可能很有用,但通常应该避免用于异步任务。只剩下 LongRunning 和 PreferFairness,它们都是优化提示,只能在应用程序分析后指定。我经常看到 LongRunning 特别被滥用。在绝大多数情况下,线程池会在 0.5 秒内适应任何长时间运行的任务——没有 LongRunning 标志。很可能,你并不真正需要它。
您在这里遇到的真正问题是您的操作实际上并不是长期运行的。您正在做的实际工作是异步操作,这意味着它基本上会立即返回给调用者。因此,在让任务调度程序安排它时,您不仅不需要使用长时间运行的提示,甚至不需要使用线程池线程来完成这项工作,因为它基本上是瞬时的。您根本不应该使用StartNewor ,更不用说使用长时间运行的标志了。Run
因此,您可以通过调用异步方法在当前线程上启动它,而不是采用异步方法并在另一个线程中启动它。卸载已经异步操作的启动只会产生更多工作,从而使速度变慢。
所以你的代码一直简化为:
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) { }
}
}
Run Code Online (Sandbox Code Playgroud)