0

I have a windows service (using Topshelf) which hosts a WCF service (created using SimpleInjectorServiceHost) and also spins up threads (using Task.Run).

The idea is to:

  • WCF service receives commands
  • post those commands to BufferBlock<T>. The service needs this BufferBlock<T> injected into its constructor
  • threads are supposed to be TransformBlock<T>'s which will do the required action and return result
  • this result needs to be read back by the WCF service method and returned back to the caller

I am just starting with TPL Dataflow so maybe my understanding of action blocks is totally wrong

I spin the threads in the windows service OnStart method

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

IThreadHost/ThreadHost is a simple interface/class that has methods for each command type. DoWork is supposed to wait on the BufferBlock and process each item once available.

The WCF service is started using

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

Bootstrapper.CreateServiceHost simply abstracts away new SimpleInjectorServiceHost(_container, serviceType).

Q: Should the BufferBlock<T> be a singleton registered with SimpleInjector?

Q: How to set DoWork of each threadHost as the TransformBlock<T> and link to the BufferBlock<T>?

Q: How do I get output of the TransformBlock<T> in my WCF service method? WCF service methods writes the command to BufferBlock<T> and then needs to wait for the output somehow?

VMAtm
  • 27,943
  • 17
  • 79
  • 125
user2321864
  • 2,207
  • 5
  • 25
  • 35

1 Answers1

3

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. If you want, you can optimize it's usage for your needs with options.

What you got here in your solution is a general Producer/Consumer pattern, which can be done much more easily with TPL DataFlow. So the answer for your first question:

Q: Should the BufferBlock be a singleton registered with SimpleInjector?

Yes, either BufferBlock and TransformBlock should be singletons, but it depends on the answer for third question

As I said already, you really don't need threads with TPL and TPL DataFlow, so second question is easy to answer too:

Q: How to set DoWork of each threadHost as the TransformBlock and link to the BufferBlock?

You should use no threads in your solution and direct linking the TransformBlock to the 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 service methods writes the command to BufferBlock and then needs to wait for the output somehow?

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.

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.

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. Please note that in this case of implementation you really don't need the TPL DataFlow at all.

VMAtm
  • 27,943
  • 17
  • 79
  • 125