使用Task.ContinueWith创建执行队列?

8

我有几个需要在后台执行的操作,但它们必须按顺序同步执行。

我想知道是否使用Task.ContinueWith方法来实现这一点是一个好主意。您是否预见到任何问题?

我的代码大致如下:

private object syncRoot =new object();
private Task latestTask;

public void EnqueueAction(System.Action action)
{
    lock (syncRoot)
    {
        if (latestTask == null)
            latestTask = Task.Factory.StartNew(action);
        else
            latestTask = latestTask.ContinueWith(tsk => action());
    }
}
3个回答

9
这种方法存在一个缺陷,我最近也发现了这个问题,因为我也在使用这种方法确保任务按顺序执行。在我的应用程序中,我有成千上万个这些迷你队列的实例,并很快发现我遇到了内存问题。由于这些队列经常处于空闲状态,我长时间持有最后完成的任务对象,从而阻止了垃圾回收。由于最后完成任务的结果对象通常超过85,000字节,它被分配到大对象堆(在垃圾回收期间不执行压缩)。这导致LOH的碎片化,并且进程不断增长。作为一种解决方法,您可以在锁定内的真实任务之后安排一个无操作任务。对于一个真正的解决方案,我需要采用一种不同的控制调度方法。

5

这应该按设计工作(利用TPL会立即安排后续任务,如果相应的任务已经完成)。

在这种情况下,我个人会使用专用线程使用并发队列(ConcurrentQueue)来提取任务 - 这更明确,但更容易解析读取代码,特别是如果您想找出当前有多少个任务在队列中等待执行等信息。


1
一个改进的方法是在ConcurrentQueue周围使用BlockingCollection。然后可以调度一个任务/工作线程,只需在阻塞集合上使用foreach即可。foreach将“挂起”,直到队列中添加了新项或在阻塞集合上调用CompleteAdding()。 - Martin Ullrich

0

我使用了这个代码片段,似乎已经按照设计工作了。

在我的情况下,实例的数量不会达到数千个,而是单个数字。

尽管如此,目前还没有出现任何问题。

如果有的话,我会对ConcurrentQueue示例感兴趣。

谢谢


对于一个超级简单的队列,这个方法对我来说仍然有效。但是对于更复杂的任务队列,我开始更多地使用Dataflow - lukebuehler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接