.NET任务并行库

时间:2012-04-05 10:38:36

标签: c# .net multithreading parallel-processing task

我已经阅读了有关TPL的文档和许多教程,但没有一篇涵盖我想要实现的模型。

某些算法总是有固定的迭代次数。

我需要不断运行线程(尽可能多):

而(真)

  • 从MAIN主题获取数据
  • 执行繁重的耗时任务(在单独的线程中)
  • 更新MAIN主题信息

另外我需要能够设置闹钟(例如5秒)的机制。五秒钟后,所有工作必须暂停一段时间,然后重新开始。

我应该使用Task.Continue与相同的任务吗?但我没有处理上一个任务启动的结果,而是更新MAIN线程中的数据结构,然后决定新任务迭代的输入...

我如何决定应该为最佳效率创建多少任务?

不,我正在使用BackgroundWorkers,因为他们有很好的RunEventCompleted事件 - 在我的主线程中,我可以更新我的MAIN结构,检查时间限制,然后最终在完成的BackgroundWorker上再次调用StartAsync。它很好而且清晰,但可能非常有用。 我需要在多处理器,多核服务器上实现高效率。

一个问题是计算总是在线,永远不会停止。还有一些网络,可以远程询问MAIN结构的当前状态。

第二个问题是关键的时间控制(我必须有精确的计时器 - 当它停止时,没有线程可以重新启动)。然后在结束后进行特殊的高优先级任务,恢复所有工作。

第三个问题是操作没有上限。

这三个约束,从我观察到的,并没有很好地进行TPL - 我不能使用像Parallel.For这样的东西,因为集合是由任务本身的实时结果修改的...... 我不知道如何结合:

  • 让TPL决定应创建多少线程的能力
  • 有一些生命周期运行的线程(连续重启之间有暂停和同步点)
  • 在开始时只创建一次线程(它们应该只用不断的新参数重新启动)

有人可以给我提供线索吗? 我知道怎么做坏事,无所事事。我描述了一些小的要求,这使我无法做到这一点。我有点困惑。

2 个答案:

答案 0 :(得分:3)

您需要使用消息+演员+调度程序imo。然后你需要使用一种能够满足它的语言。查看从Azure Service Bus异步接收的this code,在共享队列中排队并通过actor管理运行时状态。

内联:

  

我应该使用Task.Continue与相同的任务吗?

不,ContinueWith会根据每个延续传递中的异常处理来杀死你的程序;在TPL中没有好办法将失败的状态编组到呼叫方/主线程中。

  

但我没有处理上一个任务启动的结果,但是   而是更新数据结构   主线程然后决定新任务的输入是什么   迭代...

除非你愿意花很多时间来解决这个问题,否则你需要超越线程。

  

我如何决定应该为TPL决定创建多少任务   最佳效率?

这是由运行异步工作流的框架处理的。

  

不,我使用的是BackgroundWorkers,因为他们很好   RunEventCompleted事件 - 在我的主线程中,我可以   更新我的MAIN结构,检查时间限制,然后最终   在已完成的BackgroundWorker上再次调用StartAsync。它是   很好,很清楚,但可能非常有用。我需要做到   在多处理器,多核服务器上高效。

     

一个问题是计算总是在线,永远不会停止。那里   也是一些网络,它可以远程询问当前   主要结构状态。第二个问题是关键时间控制(I   必须有精确的计时器 - 当它停止时,没有线程可以   重新启动)。

如果以异步方式运行所有内容,则可以将消息传递给暂停它的actor。你安排的演员负责用他们的schedulled消息调用所有的订阅者;查看链接代码中的paused状态。如果您有未完成的请求,您可以向他们传递取消令牌并处理“硬”取消/套接字中止。

  

然后在它结束后进入特殊的高优先级任务   工作重新开始。从我观察到的这两个约束来看,没有   顺利进行TPL - 我不能使用Parallel.For因为   该集合由任务本身的实时结果修改......

您可能需要一种称为管道和过滤器的模式。你将你的输入输入一系列工人(演员);每个工人都从其他工人的产出中消费。信令是使用控制通道完成的(在我的情况下是演员的收件箱)。

答案 1 :(得分:0)

我认为你应该阅读

MSDN: How to implement a producer / consumer dataflow pattern

我遇到了同样的问题:一个生产者生产了物品,而一些消费者消费它们并决定将它们发送给其他消费者。每个消费者都是异步工作,独立于其他消费者。

你的主要任务是制片人。他生产其他任务应该处理的项目。包含主任务代码的类具有以下功能:

public async Task ProduceOutputAsync(...)

您的主程序使用以下命令启动此任务:

var producerTask = Task.Run( () => MyProducer.ProduceOutputAsync(...)

一旦调用它,生产者任务就开始产生输出。与此同时,您的主程序可以继续做其他事情,例如启动消费者。

但是,让我们首先关注制作人任务。

生产者任务生成要由其他任务处理的类型T的项目。它们使用实现ITargetBlock'。

的对象转移到另一个任务

每次生产者任务完成创建类型为T的对象时,它都会使用ITargetBlock.Post将其发送到目标块,或者最好是异步版本:

while (continueProducing())
{
    T product = await CreateProduct(...)
    bool accepted = await this.TargetBlock(product)
    // process the return value
}
// if here, nothing to produce anymore. Notify the consumers:
this.TargetBlock.Complete();

制作人需要ITargetBlock <T&gt;。在我的应用程序中,BufferBlock <T&gt;够了。检查MSDN以获取其他可能的目标。

无论如何,数据流程块也应该实现ISourceBlock <T&gt;。您的接收器等待输入到达源,获取并处理它。完成后,它可以将结果发送到它自己的目标块,并等待下一个输入,直到不再有预期的输入。当然,如果您的消费者没有产生输出,那么它就不必向目标发送任何东西。

等待输入如下:

ISourceBlock`<T`> mySource = ...;
while (await mySource.ReceiveAsync())
{   // a object of type T is available at the source
    T objectToProcess = await mySource.ReceiveAsync();
    // keep in mind that someone else might have fetched your object
    // so only process it if you've got it.
    if (objectToProcess != null)
    {
        await ProcessAsync(objectToProcess);

        // if your processing produces output send the output to your target:
        var myOutput = await ProduceOutput(objectToprocess);
        await myTarget.SendAsync(myOutput);
    }
}
// if here, no input expected anymore, notify my consumers:
myTarget.Complete();
  • 构建您的制作人
  • 构建所有消费者
  • 给生产者一个BufferBlock将其输出发送到
  • 启动制作人MyProducer.ProduceOutputAsync(...)
  • 当生产者生成输出并将其发送到缓冲区块时:
  • 为消费者提供相同的BufferBlock
  • 将消费者作为单独的任务启动
  • 等待Task.WhenAll(...)等待所有任务完成。

每个消费者一听到就不再有任何输入就会停止。 完成所有任务后,您的主要功能可以读取结果并返回

相关问题