【问题标题】:The "bag of tasks" concept in C#, enqueue,pause,cancel logical tasksC#中的“任务包”概念,入队、暂停、取消逻辑任务
【发布时间】:2013-08-20 15:05:08
【问题描述】:

我正在开发的应用程序是这样组成的: 生产者任务扫描文件系统中的文本文件并将对它们的引用放入包中。 许多消费者任务同时从包中获取文件引用并读取文件(并对其内容做一些简短的工作)

我必须能够暂停和恢复整个过程。

我尝试过使用 TPL,为每个放入包中的文件 ref 创建一个任务(在这种情况下,包只是一个概念,生产者在查找文件时直接创建消费者任务)但是这样我无法控制我创建的任务,我不能(或者我不知道如何)暂停它们。我可以编写一些代码来暂停当前正在执行任务的线程,但这会破坏处理逻辑任务而不是手动创建线程的意义,不是吗?我想要类似“已经分配给物理线程的任务可以完成,但等待的逻辑任务在恢复命令之前不应启动”

我怎样才能做到这一点?可以用 TPL 完成还是我应该使用其他东西?

编辑: 您的答案都是有效的,但我的主要疑问仍未得到解答。我们正在谈论任务,如果我使用 TPL,我的生产者和我的许多消费者将是任务(对吗?)而不是线程(好吧,在执行的那一刻,任务将映射到线程上)。我发现的每一种同步机制(如评论“ManualResetEventSlim”中提出的那种)都在线程级别工作。

例如“ManualResetEventSlim”的Wait()方法的描述是“阻塞当前线程,直到当前ManualResetEventSlim被设置。”

我对任务的了解纯粹是学术性的,我不知道“现实世界”中的事情是如何运作的,但对我来说,我需要一种在任务中协调(等待/信号/...)任务的方法似乎是合乎逻辑的级别或事情可能会变得很奇怪......就像......两个任务可能映射在同一个线程上,但一个应该发出信号,另一个正在等待然后死锁。我有点困惑。这就是为什么我问我的应用是否可以使用 TPL 代替老式简单线程。

【问题讨论】:

  • 是的,您必须自己创建一个暂停机制。使用例如消费者反复等待的ManualResetEventSlim,生产者设置/重置为暂停/恢复。
  • 普通的Task 总是在单个线程上执行。因此,任何适用于Threads 的同步原语也适用于Tasks。
  • @svick 如果我启动 100 个执行包含 Wait() 的相同代码的任务怎么办?根据对这个问题的接受答案stackoverflow.com/questions/14515207/… 将创建 100 个等待线程,好吗?
  • @flagg19 我认为这个答案是错误的(至少对于.Net 4.5)。但是如果你不调用ThreadPool.SetMaxThreads(),那么是的,最终会创建100个线程。这并不理想,所以如果你不想那样,你将不得不做一些事情。要么在集合中有代表而不是Tasks,然后有有限数量的Tasks 来处理它们(你可以使用Parallel.ForEach())。另一种选择是使用a custom TaskScheduler with limited degree of parallelism。
  • @svick 谢谢,我想我会选择“在集合中有代表而不是任务,然后处理它们的任务数量有限”

标签: c# task-parallel-library .net-4.5


【解决方案1】:

是的,你可以这样做。首先,你有一个主线程,你的应用程序。那里有两个工人,由线程表示。第一个工人是生产者,第二个工人是消费者。

当您的应用程序启动时,您会启动工作程序。它们都对并发集合,包进行操作。生产者搜索文件并放置对包的引用,消费者从包中获取引用并启动每个引用的任务。

当您想发出暂停信号时,只需暂停生产者即可。如果你这样做,如果袋子里什么都没有,消费者也会停止工作。如果这不是所需的行为,您可以简单地定义生产者的暂停也会清除包 - 先备份您的包,然后再清除它。这样所有正在运行的任务都会完成它们的工作,消费者不会开始新的任务,但它仍然可以运行并等待结果。

编辑:

根据您的编辑。我不知道如何以您想要的方式实现它,但是尽管尝试使用新技术很好,但不要让您的头脑蒙上阴影。使用ThreadPool 也是一件好事。启动应用程序需要更多时间,但一旦运行,消耗会更快,因为您已经准备好工作人员。

这不是一个坏主意,您可以指定最大工人数。如果您为包中的每个项目创建一个任务,它将更加消耗内存,因为您仍然会分配和释放内存。 ThreadPool 不会发生这种情况。

【讨论】:

  • 这不是我正在寻找的答案,但感谢您清理/备份包的想法。
  • 是的,在我看来,这是一个更好的解决方案。如果您发送通知以暂停应用程序,请清理您的包并让您的消费者线程使用 Thread.Sleep 自行暂停(如果包中没有任何内容)。不过要看你的要求了。有了并发,您可以尝试大量的解决方案来优化您的应用程序。
【解决方案2】:

当然,您可以为此使用 TPL。也可能是reactive extensions 和 LINQ 来简化分组和pausing/resuming 线程工作。

如果您对每个文件只有一个简短的工作,那么最好不要因取消而干扰处理程序函数。您可以改为暂停工作人员排队。

我想像这样:

  • 您的目录扫描线程将找到的文件放入可观察的集合中。
  • 消费者线程订阅集合更改并获取/删除文件并将它们分配给工作人员。

【讨论】:

    猜你喜欢
    • 2014-11-04
    • 2015-06-12
    • 2015-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-04
    • 1970-01-01
    相关资源
    最近更新 更多