如何使用任务并行库来管理任务列表

3

我正在尝试管理一个Windows服务的任务列表,这样当我关闭服务时,我可以使用Task.WaitAll()方法来防止服务关闭直到所有剩余的任务完成。例如,我有一个Run()方法,它会执行直到更新布尔变量:

public void Run()
{
    while (runFlag)
    {
        if (MaxTasksAchieved)
        {
            System.Threading.Thread.Sleep(pollingInterval);
        }
        else
        {

            taskList.Add(Task.Factory.StartNew(() =>
            {
                // do stuff
            }));
        }
    }
}

然后在我的Stop()方法中,我有以下内容:

public void Stop()
{
    runFlag = false;

    if (taskList.Count > 0)
    {
        // wait
        Task.WaitAll(taskList.ToArray());
    }
    else
    {
        // no wait, great.
    }
}

我的问题是如何优雅地让任务在执行完成后自动从列表中删除?我希望任务能从列表中删除,这样当调用Stop()时,taskList只包含当前正在进行的任务。

1
你的 if (MaxTasksAchieved) 和轮询循环可以方便地被任务调度器的 MaxDegreeOfParallelism 所替代。 - Asad Saeeduddin
2
关于删除单个任务,由于您使用了WaitAll,这表示所有任务都已完成,为什么不能只丢弃/忽略列表并继续进行所需的操作?为什么需要清空列表? - Asad Saeeduddin
@Asad 我觉得我缺少一个关键信息。所以这个服务是无限运行的,当它拾取要处理的项目时,它会触发一个任务并将其添加到列表中进行管理。一旦任务完成,我想从列表中删除它,这样如果调用Stop(),它只等待当前正在进行的任务。希望这能让你更好地理解我的问题。 - farbodg
4个回答

1
为什么不直接使用?
public void Stop()
{
   Task.WaitAll(taskList.ToArray());
 }

如果任务列表为空,服务将立即关闭。

没错...但我正在向我的列表中添加项目,而不是删除。这会有问题吗?一旦处理完成,我应该从列表中删除任务,除非我没有理解TPL的工作原理。另外,如果服务无限期运行,我的列表不会变得非常庞大并占用所有内存吗? - farbodg
1
@FarbodGolkar 不需要将已完成的任务从任务列表中删除。Task.WaitAll等待输入列表中的所有任务完成,如果任务已经完成,则无需等待,因此Task.WaitAll会继续执行下一个任务,直到所有任务都完成执行。如果您想限制并行任务的数量,则应查看使用 Parallel.ForEach 并使用此处描述的并行选项 MaxDegreeOfParallelism - ziddarth

1

由于WaitAll()方法会等待所有任务完成,因此您可以重新初始化taskList....

    public void Stop()
    {
        runFlag = false;

        if (taskList.Count > 0)
        {
            // wait
            Task.WaitAll(taskList.ToArray());

            taskList = new List<Task>();
        }
        else
        {
            // no wait, great.
        }
    }

1

正如其他人所说,完成任务后从列表中删除可能是不必要的。但是,如果你一定要这样做,可以使用continuation。

Task myTask = new Task(() => {
    // do stuff
});
myTask.ContinueWith(
    t => { taskList.Remove(t); }
);

1
如果您想从列表中删除任务,只需添加一条继续执行该操作即可:
taskList.Add(task.ContinueWith(t = > taskList.Remove(t)));

但是你可以简单地使用Task.WaitAll在所有任务上,因为已经完成的任务已经完成了。

Task.WaitAll(taskList.ToArray());

您可以通过筛选任务状态列表来轻松等待正在运行的任务。
Task.WaitAll(taskList.Where(t => t.Status != TaskStatus.RanToCompletion).ToArray());

假设我不从列表中删除任务,那么列表可能会变得非常大,以至于我的服务器会耗尽内存吗?这是我在这里的主要关注点。 - farbodg
1
@FarbodGolkar 是的。这是可能的。虽然不太可能,但确实有可能。如果您担心这个问题,那么请移除ContinueWith任务。 - i3arnon

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