简体   繁体   English

如何在API中将实现生产者使用者管道的类暴露给客户端代码?

[英]How to expose a class implementing producer consumer pipeline to client code in an API?

I'm a bit confused on how to expose a class that implements a producer consumer pipeline to client code. 我对如何公开一个将生产者使用者管道实现到客户端代码的类感到困惑。

Let's say I have two classes that represents the producer and consumer, like this: 假设我有两个代表生产者和消费者的类,如下所示:

public class Consumer
{
  ...

  public void Cosume();

  ...
}

Then the producer: 然后生产者:

public class Producer
{
  ...

  public void Produce();

  ...
}

The there is a third class that orchestrate the producer and the consumer (and here comes the problem, by the way) 第三类协调生产者和消费者(顺便问一下问题来了)

public class ProducerConsumer
{
  ...

  private Producer producer;
  private Consumer consumer;

  ...

  public void Start()
  {
     ...
  }
}

How should I implement start? 我应该如何实施启动?

I was thinking to call producer.Produce() and consumer.Consume() wrapped in Task.Run and the await for them to complete, like so: 我正在考虑调用包装在Task.Run中的producer.Produce()和consumer.Consume(),并等待它们完成,如下所示:

public async Task Start()
{
     await Task.WhenAll(Task.Run(() => producer.Produce(), Task.Run(() => consumer.Consume());
}

But I've read that having Task.Run() in the implementation is not a very good practice, so I've discarded it. 但是我已经读到,在实现中包含Task.Run()并不是一种很好的做法,因此我将其丢弃。

I've also thought of Parallel.Invoke(), and leave the responsibility to offload to a different Thread the blocking wait of Parallel.Invoke() to the client code, something like this: 我还想到了Parallel.Invoke(),并把将Parallel.Invoke()的阻塞等待转移到另一个线程的责任留给了客户端代码,如下所示:

public void Start()
{
      Parallel.Invoke(() => producer.Produce(), () => consumer.Consume());
}

And the the client code would do something like: 客户端代码将执行以下操作:

public async void ButtonHandler(object sender, RoutedEventArgs e)
{
   await Task.Run(() => producerConsumer.Start());
}

But offloading to a different Thread just to wait two other thread to finish looked a bit strange to me because I'm wasting a Thread just to wait something. 但是卸载到另一个线程只是为了等待另外两个线程完成对我来说有点奇怪,因为我在浪费一个线程只是为了等待一些东西。

After reading other questions on StackOverflow many suggested to leave the responsibility on how to call the parallel code to the calling code, so I thought to expose the Producer and the Consumer from the ProducerConsumer class and let the client code call Producer.Produce() and Consumer.Consume() the way it wants, more or less like: 在阅读了关于StackOverflow的其他问题之后,许多人建议把责任放在如何将并行代码调用到调用代码上,因此我想从ProducerConsumer类中公开Producer和Consumer,然后让客户端代码调用Producer.Produce()和Consumer.Consume()按其所需的方式,或多或少像:

public async void ButtonHandler(object sender, RoutedEventArgs e)
{
   Task producerTask = Task.Run(() => producerConsumer.Producer.Produce());
   Task consumerTask = Task.Run(() => producerConsumer.Consumer.Consumer());

   await Task.WhenAll(producerTask, consumerTask);
}

But this last implementation looks awkward to me because the caller code is responsible to call Produce() and Consume() which could lead to errors if one of the two method is omitted. 但是最后一个实现对我来说很尴尬,因为调用者代码负责调用Produce()和Consume(),如果省略这两个方法之一,则可能导致错误。

I know about TPL.DataFlow to implement a producer consumer pipeline but I can't add external dependencies to the library. 我知道TPL.DataFlow可以实现生产者使用者管道,但无法将外部依赖项添加到库中。

So how should I write the Start() method? 那么我应该如何编写Start()方法呢?

And more in general: how should I expose code that is parallel by its nature to library client code? 以及更一般的说:我应该如何将本质上与库客户端代码并行的代码公开?

If you have an async -compatible producer/consumer queue (eg, BufferBlock<T> from TPL Dataflow), and your producer is throttled (eg, the queue has a reasonable maximum number of elements, or the producer's data comes from some I/O operation), then you can make your producer and consumer async and call them directly like this: 如果您具有async兼容的生产者/消费者队列(例如,TPL Dataflow中的BufferBlock<T> ),并且生产者受到限制(例如,队列中具有合理的最大元素数,或者生产者的数据来自某些I / O操作),则可以使生产者和消费者async ,并直接像这样调用它们:

public async Task ExecuteAsync()
{
  await Task.WhenAll(producer.ProduceAsync(), consumer.ConsumeAsync())
      .ConfigureAwait(false);
}

Otherwise, you'll have to "give" somewhere. 否则,您将不得不“放弃”某个地方。 If your producer and consumer are synchronous, then your containing class - by definition - controls two threads. 如果生产者和使用者是同步的,那么包含类(按定义)将控制两个线程。 And in that case it's fine to use Task.Run : 在这种情况下,可以使用Task.Run

public async Task ExecuteAsync()
{
  await Task.WhenAll(Task.Run(() => producer.Produce()), Task.Run(() => consumer.Consume()))
      .ConfigureAwait(false);
}

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

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