TPL Dataflow ProducerConsumer Pattern

时间:2013-01-10 10:25:31

标签: .net task-parallel-library tpl-dataflow

使用TPL DataFlow编写了一个示例生产者消费者模式。我在这里有一些基本问题。

  1. 只有在从制作人发布所有项目后,才会激活消费者。异步意味着生成和使用任务都可以并行运行。

  2. 给定消费者的睡眠时间以验证其是否阻止其他数据项。它似乎是顺序执行而没有任何并行性。

  3. 我在这里做错了吗?

    class AscDataBlocks
    {
        public Int64 start;
        public Int64 End;
        //public string ThreadName;
        public void AscBufferProducer(ITargetBlock<Int64> targetAscTransform) 
        // This is using TPL DataBlock producer consumer pattern.
        {
            for (var i = start; i < End; i++)
            {
                Console.WriteLine("Postingasc : {0}", i);
                targetAscTransform.Post(i);
            }
        }
    
        public void ProcessDataBuffer(Int64 ascDataSet)
        {
            if (ascDataSet == 5) 
            // Testing if this will delay all the other data processing
                Thread.Sleep(5000);
            else
                Thread.Sleep(500);                
            Console.WriteLine(ascDataSet);
        }
    
        // Demonstrates the consumption end of the producer and consumer pattern. 
        public async Task<Int64> AscTransConsumerAsync(IReceivableSourceBlock<Int64> source)
        {
            // Initialize a counter to track the number of bytes that are processed. 
            int status = 0;
    
            // Read from the source buffer until the source buffer has no  
            // available output data. 
            while (await source.OutputAvailableAsync())
            {
                Int64 data;
                source.TryReceive(out data);
                ProcessDataBuffer(data);//This function processed the data buffer for ascollection and writes the data to the database.
                // Increment the count of bytes received.
                status = 1;
            }
            return status;
        }
    }
    
    static void Main(string[] args)
    {
        AscDataBlocks ascb;
        BufferBlock<Int64> ascbuffer;
        System.Threading.Tasks.Task<Int64> ascProcessingconsumer;
        CancellationToken ct = new CancellationToken();
        CancellationTokenSource cts = new CancellationTokenSource();
        ascb = new AscDataBlocks();
        ascb.start = 1;
        ascb.End = 100;           
        try
        {
            ascbuffer = new BufferBlock<Int64>(new ExecutionDataflowBlockOptions
            {
                MaxDegreeOfParallelism = 5,
                CancellationToken = ct
            });// Initiallize the buffer bloack
            ascProcessingconsumer = ascb.AscTransConsumerAsync(ascbuffer); //Initialize the consumer.
            //Post source data to the dataflow block.
            ascb.AscBufferProducer(ascbuffer);
            ascProcessingconsumer.Wait();
        }
        catch (Exception ex)
        {
            //foreach (var v in ex.InnerExceptions)
            //    Console.WriteLine("msg: " + v.Message);                
        }            
    }  
    

1 个答案:

答案 0 :(得分:4)

  

只有在从制作人发布所有项目后,消费者才会处于活动状态。异步意味着生成和使用任务都可以并行运行。

这是因为您在消费者有机会开始之前非常快速地发布了所有项目。如果您添加了Thread.Sleep(100),您会发现它们实际上并行工作。

  

给定消费者的睡眠时间以验证其是否阻止其他数据项。它似乎是顺序执行而没有任何并行性。

TPL Dataflow并不神奇:它不会修改您的代码以便并行执行。是你调用AscTransConsumerAsync()一次,所以不要惊讶它实际只执行一次。

TDF确实支持并行处理,但您需要实际让它执行处理代码。为此,请使用其中一个执行块。在您的情况下,ActionBlock似乎是合适的。

如果您使用它,则可以通过设置MaxDegreeOfParallelism将块配置为并行执行。当然,这样做意味着您需要确保处理委托是线程安全的。

有了这个,AscTransConsumerAsync()现在可能看起来像:

public async Task<Int64> AscTransConsumerAsync(ISourceBlock<Int64> source)
{
    // counter to track the number of items that are processed
    Int64 count = 0;

    var actionBlock = new ActionBlock<Int64>(
        data =>
        {
            ProcessDataBuffer(data);
            // count has to be accessed in a thread-safe manner
            // be careful about using Interlocked,
            // for more complicated computations, locking might be more appropriate
            Interlocked.Increment(ref count);
        },
        // some small constant might be better than Unbounded, depedning on circumstances
        new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });

    source.LinkTo(actionBlock, new DataflowLinkOptions { PropagateCompletion = true });

    // this assumes source will be completed when done,
    // you need to call ascbuffer.Complete() after AscBufferProducer() for this
    await actionBlock.Completion;

    return count;
}