简体   繁体   English

TPL DataFlow缠住我的头

[英]Wrapping my head around TPL DataFlow

I have a windows service (using Topshelf ) which hosts a WCF service (created using SimpleInjectorServiceHost ) and also spins up threads (using Task.Run ). 我有一个Windows服务(使用Topshelf ),该服务承载WCF服务(使用SimpleInjectorServiceHost创建),并且启动线程(使用Task.Run )。

The idea is to: 这个想法是:

  • WCF service receives commands WCF服务接收命令
  • post those commands to BufferBlock<T> . 将这些命令发布到BufferBlock<T> The service needs this BufferBlock<T> injected into its constructor 服务需要将此BufferBlock<T>注入其构造函数中
  • threads are supposed to be TransformBlock<T> 's which will do the required action and return result 线程应该是TransformBlock<T> ,它将执行所需的操作并返回结果
  • this result needs to be read back by the WCF service method and returned back to the caller 此结果需要由WCF服务方法读取并返回给调用方

I am just starting with TPL Dataflow so maybe my understanding of action blocks is totally wrong 我只是从TPL Dataflow开始,所以也许我对操作块的理解是完全错误的

I spin the threads in the windows service OnStart method 我旋转Windows服务OnStart方法中的线程

for(var i = 0, i < Environment.ProcessorCount, ++i)
{
    Task.Run(() =>
    {
        using (var scope = Bootstrapper.BeginScope())
        {
            var threadHost = Bootstrapper.GetInstance<IThreadHost>();
            threadHost.DoWork(_cancellationTokenSource.Token);
        }
    }, _cancellationTokenSource.Token);
};

Bootstrapper.BeginScope simply abstracts away SimpleInjector's _container.BeginLifetimeScope() method Bootstrapper.BeginScope简单地抽象出SimpleInjector的_container.BeginLifetimeScope()方法

IThreadHost / ThreadHost is a simple interface/class that has methods for each command type. IThreadHost / ThreadHost是一个简单的接口/类,具有每种命令类型的方法。 DoWork is supposed to wait on the BufferBlock and process each item once available. DoWork应该等待BufferBlock并在可用时处理每个项目。

The WCF service is started using 使用以下命令启动WCF服务

if (_serviceHost != null)
{
    _serviceHost.Close();
}
_serviceHost = Bootstrapper.CreateServiceHost(typeof(NodeService));
_serviceHost.Open();

Bootstrapper.CreateServiceHost simply abstracts away new SimpleInjectorServiceHost(_container, serviceType) . Bootstrapper.CreateServiceHost只是抽象出new SimpleInjectorServiceHost(_container, serviceType)

Q: Should the BufferBlock<T> be a singleton registered with SimpleInjector ? 问:如若BufferBlock<T>与登记的单SimpleInjector

Q: How to set DoWork of each threadHost as the TransformBlock<T> and link to the BufferBlock<T> ? 问:如何将每个threadHost DoWork设置为TransformBlock<T>并链接到BufferBlock<T>

Q: How do I get output of the TransformBlock<T> in my WCF service method? 问:如何在WCF服务方法中获取TransformBlock<T>输出? WCF service methods writes the command to BufferBlock<T> and then needs to wait for the output somehow? WCF服务方法将命令写入BufferBlock<T> ,然后需要以某种方式等待输出?

I think you a little bit over-complicating this. 我认为您对此有些复杂。 You really do not need a threads for your TransformBlock workers - each block in TPL DataFlow already has a corresponding task. 您确实不需要为TransformBlock工作线程使用线程-TPL DataFlow中的每个块已经具有一个相应的任务。 If you want, you can optimize it's usage for your needs with options . 如果需要,可以使用options优化它的用法

What you got here in your solution is a general Producer/Consumer pattern , which can be done much more easily with TPL DataFlow. 解决方案中提供的是通用的Producer/Consumer模式 ,可以使用TPL DataFlow轻松完成。 So the answer for your first question: 因此,第一个问题的答案是:

Q: Should the BufferBlock be a singleton registered with SimpleInjector? 问:BufferBlock是否应为在SimpleInjector中注册的单例?

Yes, either BufferBlock and TransformBlock should be singletons, but it depends on the answer for third question 是的, BufferBlockTransformBlock都应该是单例,但这取决于第三个问题的答案

As I said already, you really don't need threads with TPL and TPL DataFlow , so second question is easy to answer too: 就像我已经说过的,您真的不需要TPLTPL DataFlow线程,因此第二个问题也很容易回答:

Q: How to set DoWork of each threadHost as the TransformBlock and link to the BufferBlock? 问:如何将每个threadHost的DoWork设置为TransformBlock并链接到BufferBlock?

You should use no threads in your solution and direct linking the TransformBlock to the BufferBlock 您不应在解决方案中使用任何线程,而应直接将TransformBlock链接到BufferBlock

As for third question, here is the interesting part, because the answer is: 至于第三个问题,这是有趣的部分,因为答案是:

Q: How do I get output of the TransformBlock in my WCF service method? 问:如何在WCF服务方法中获取TransformBlock的输出? WCF service methods writes the command to BufferBlock and then needs to wait for the output somehow? WCF服务方法将命令写入BufferBlock,然后需要以某种方式等待输出?

It depends. 这取决于。

Main idea of data processing with TPL DataFlow is that it is asynchronous, and you can't be definitely sure that what you have on exit of the TransformBlock is the result exactly yours request - there could be other data being transformed with other requests, and simply polling the output with OutputAvailableAsync() could be quite inefficient method for your solution. 使用TPL DataFlow进行数据处理的主要思想是它是异步的,您不能肯定地确定在TransformBlock退出时所拥有的正是您所请求的结果-可能有其他数据正在被其他请求转换,并且仅使用OutputAvailableAsync()轮询输出对于您的解决方案而言可能是效率很低的方法。

In this case you should create some kind of dictionary for processing results so you can easily get information, is your request done or not. 在这种情况下,您应该创建某种字典来处理结果,以便您轻松获取信息(无论请求是否完成)。 In this case of implementation you can return the id for request, and introduce another service method for getting the results by that id. 在这种情况下,您可以返回请求的ID,并引入另一种服务方法以通过该ID获取结果。

Another approach is incapsulate data flow into one helper class, which will contains it's own copy of BufferBlock and TransformBlock , and in this case you can create a async method using polling, as in linked example. 另一种方法是将数据流封装到一个帮助器类中,该类将包含它自己的BufferBlockTransformBlock副本,在这种情况下,您可以使用轮询创建一个async方法,如链接示例中所示。 Please note that in this case of implementation you really don't need the TPL DataFlow at all. 请注意,在这种情况下,您实际上根本不需要TPL DataFlow。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM