我有一个包含
BlockingCollection<Task>
的类,用于对多个任务进行排队以按顺序运行(先进先出)。
队列本身是在单独的长时间运行任务上启动的。
public class TaskQueue
{
private readonly BlockingCollection<Task> _queue = new BlockingCollection<Task>();
public TaskQueue()
{
Task.Factory.StartNew(() =>
{
ProcessQueue();
}, TaskCreationOptions.LongRunning);
}
private async Task ProcessQueue()
{
foreach (var task in _queue.GetConsumingEnumerable())
await task;
}
...
}
当应用程序启动时,我创建了许多
TaskQueue
实例,因为我需要为应用程序的不同组件使用不同的队列。
我看到的问题是,即使
ProcessQueue()
是在长时间运行的任务 .NET Long Running Task
上启动的,在任务的第一次等待之后,它会返回到 .NET ThreadPool Worker
,即不再是长时间运行的任务。
这会导致
TaskQueue.ProcessQueue
的所有实例都以 .NET ThreadPool Workers
结束(在第一次等待之后),然后导致应用程序启动新任务的速度非常慢,因为它已达到并发运行的最大数量 .NET ThreadPool Workers
(我相信这被设置为机器拥有的核心数,在我的例子中是 8),之后它只会每秒左右创建新的 .NET ThreadPool Workers
。
所以我的问题是:
Task.Factory.StartNew
?我相信这可以解决问题,但不确定是否有更好的方法。有没有办法返回长时间运行的任务?
是的,首先不要离开长时间运行的任务。您可以更改
ProcessQueue
的签名以返回 void
而不是 Task
,如下所示:
private void ProcessQueue()
{
foreach (var task in _queue.GetConsumingEnumerable())
task.GetAwaiter().GetResult();
}
这样,
LongRunning
任务将在等待消耗任务和等待消耗的任务完成之间交替。同一个线程将完成所有等待。
顺便说一句,
async
的原始ProcessQueue
版本,以及此处建议的void
版本,都没有做任何建设性的事情。他们没有处理任何事情,他们只是在等待。我假设您省略了该方法的“过程”部分,以保持示例简单。否则,ProcessQueue
将只是徒劳的练习。