TPL DataFlow 之一 ActionBlock

https://docs.microsoft.com/zh-cn/dotnet/standard/parallel-programming/dataflow-task-parallel-library

(一)源和目标
TPL数据流库包括数据流块,它是缓冲并处理数据的数据结构。TPL 定义了三种数据流块:源块、目标块和传播器块。
源块(System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>)作为数据源,可以读取。
目标块(System.Threading.Tasks.Dataflow.ITargetBlock<TInput>)作为数据接收方,可以写入。
传播器块IPropagatorBlock<TInput,TOutput>)作为源块和目标块,可以读取和写入。

(二)连接块[ISourceBlock<TOutput>.LinkTo()]
可以连接数据流块来形成管道(这是数据流块的线性序列),或网络(这是数据流块的图形)。管道是网络的一种形式。 在管道或网络中,当数据可用时源向目标异步传播数据。 ISourceBlock<TOutput>.LinkTo()方法将源数据流块链接到目标块。 源可以链接到零个或多个目标;目标可以从零个或多个源进行链接。您可以同时向管道或网络中添加或从其移除数据流块。 预定义的数据流块类型处理所有的建立或释放链接的线程安全性。

(三)ActionBlock
ActionBlock实现ITargetBlock,说明它是消费数据的,也就是对输入的一些数据进行处理。它在构造函数中,允许输入一个委托,来对每一个进来的数据进行一些操作。如果使用Action(T)委托,那说明每一个数据的处理完成需要等待这个委托方法结束,如果使用了Func<TInput, Task>)来构造的话,那么数据的结束将不是委托的返回,而是Task的结束。默认情况下,ActionBlock会FIFO的处理每一个数据,而且一次只能处理一个数据,一个处理完了再处理第二个,但也可以通过配置来并行的执行多个数据。ActionBlock是顺序处理数据的,这也是ActionBlock一大特性之一。主线程在往ActionBlock中Post数据以后马上返回,具体数据的处理是另外一个线程来做的。数据是异步处理的,但处理本身是同步的,这样在一定程度上保证数据处理的准确性。

image

3.1 ActionBlock同步处理示例

class Program{ static ActionBlock<string> actionBlock = new ActionBlock<string>(Print); static void Main(string[] args) { TestSync(); Console.ReadKey(); } public static void TestSync() { for (int i = 0; i < 10; i++) { actionBlock.Post(i.ToString()); } Console.WriteLine("Post finished"); } public static void Print(string str) { Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} Tid:{Thread.CurrentThread.ManagedThreadId} 长度:{actionBlock.InputCount} 【{str}】"); }

}

image

3.2  ActionBlock异步处理示例

class Program{ static ActionBlock<string> actionBlock = new ActionBlock<string>(Print1); static void Main(string[] args) { TestSync(); Console.ReadKey(); } public static void TestSync() { for (int i = 0; i < 10; i++) { actionBlock.Post(i.ToString()); } Console.WriteLine("Post finished"); } public static async Task Print1(string str) { await Task.Delay(1000); Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} Tid:{Thread.CurrentThread.ManagedThreadId} 长度:{actionBlock.InputCount} 【{str}】"); }}

image

①:以上异步基于构造函数:public ActionBlock(Func<TInput, Task> action);

②:可以用匿名异步函数来写

static ActionBlock<string> actionBlock = new ActionBlock<string>(async (str) => { await Task.Delay(1000); Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} Tid:{Thread.CurrentThread.ManagedThreadId} 长度:{actionBlock.InputCount} 【{str}】");});

3.3  指定处理器个数
如果你想异步处理多个消息的话,ActionBlock也提供了一些接口,让你轻松实现。在ActionBlock的构造函数中,可以提供一个ExecutionDataflowBlockOptions的类型,让你定义ActionBlock的执行选项,在下面了例子中,我们定义了MaxDegreeOfParallelism选项,设置为3。目的的让ActionBlock中的Item最多可以3个并行处理。

public ActionBlock(Action<TInput> action, ExecutionDataflowBlockOptions dataflowBlockOptions);public ActionBlock(Func<TInput, Task> action, ExecutionDataflowBlockOptions dataflowBlockOptions);static ActionBlock<string> actionBlock = new ActionBlock<string>((str) => { Thread.Sleep(1000); Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} Tid:{Thread.CurrentThread.ManagedThreadId} 长度:{actionBlock.InputCount} 【{str}】");

}, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 3 });

static ActionBlock<string> actionBlock = new ActionBlock<string>(async (str) => { await Task.Delay(1000); Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} Tid:{Thread.CurrentThread.ManagedThreadId} 长度:{actionBlock.InputCount} 【{str}】");}, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 3 });

image

3.4 ActionBlock生命周期
       ActionBlock也有自己的生命周期,所有继承IDataflowBlock的类型都有Completion属性和Complete方法。调用Complete方法是让ActionBlock停止接收数据,而Completion属性则是一个Task,是在ActionBlock处理完所有数据时候会执行的任务,我们可以使用Completion.Wait()方法来等待ActionBlock完成所有的任务,Completion属性只有在设置了Complete方法后才会有效。

class Program{ static ActionBlock<string> actionBlock = new ActionBlock<string>((str) => { Thread.Sleep(1000); Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} Tid:{Thread.CurrentThread.ManagedThreadId} 长度:{actionBlock.InputCount} 【{str}】"); }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 3 }); static void Main(string[] args) { TestSync(); Console.ReadKey(); } public static void TestSync() { for (int i = 0; i < 10; i++) { actionBlock.Post(i.ToString()); } actionBlock.Complete(); Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}【Post完成】"); actionBlock.Completion.Wait(); Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}【队列处理完成】"); }}

image

posted @ 2021-01-05 17:39  李华丽  阅读(1411)  评论(1编辑  收藏  举报
AmazingCounters.com