To should i use channels?
, the answer is yes, but there are other features available too.
Dataflow
.NET already offers this feature through the TPL Dataflow classes. You can use an ActionBlock class to pass messages (ie data) to a worker method that executes i the background with guaranteed order and a configurable degree of parallelism. Channels are a new feature which does essentially the same job.
What you describe is actually the simplest way of using an ActionBlock - just post data messages to it and have it process them one by one :
void Method1(MyDataObject1 data){...}
var block=new ActionBlock<MyDataObject1>(Method1);
//Start sending data to the block
for(var msg in someListOfItems)
{
block.PostAsync(msg);
}
By default, an ActionBlock has an infinite input queue. It will use only one task to process messages asynchronously, in the order they are posted.
When you're done with it, you can tell it to Complete()
and await asynchronously for all remaining items to finish processing :
block.Complete();
await block.Completion;
To handle different methods, you can simply use multiple blocks, eg :
var block1=new ActionBlock<MyDataObject1>(Method1);
var block2=new ActionBlock<MyDataObject1>(Method2);
Channels
Channels are a lower-level feature than blocks. This means you have to write more code but you get far better control on how the "processing blocks" work. In fact, you can probably rewrite the TPL Dataflow library using channels.
You could create a processing block similar to an ActionBlock with the following (a bit naive) method:
ChannelWriter<TIn> Work(Action<TIn> action)
{
var channel=Channel.CreateUnbounded<TIn>();
var workerTask=Task.Run(async ()=>{
await foreach(var msg in channel.Reader.ReadAllAsync())
{
action(msg);
}
})
var writer=channel.Writer;
return writer;
}
This method creates a channel and runs a task in the background to read data asynchronously and process them. I'm cheating "a bit" here by using await foreach
and ChannelReader.ReadAllAsync()
which are available in C#8 and .NET Core 3.0.
This method can be used like a block :
ChannelWriter<DataObject1> writer1 = Work(Method1);
foreach(var msg in someListOfItems)
{
writer1.WriteAsync(msg);
}
writer1.Complete();
There's a lot more to Channels though. SignalR for example uses them to allow streaming of notifications to the clients.