How to use System.Threading.Channels in C#
By FoxLearn 1/3/2025 9:29:49 AM 115
It provides types that facilitate efficient data exchange between producers and consumers, allowing them to operate concurrently.
Dataflow blocks vs channels
The System.Threading.Tasks.Dataflow
library is designed for pipelining, combining both storage and processing, and supports complex control flow features. In contrast, the System.Threading.Tasks.Channels
library focuses primarily on storage and is optimized for fast producer-consumer scenarios. Channels are faster than Dataflow blocks but lack some of the advanced control flow capabilities offered by Dataflow.
Why use System.Threading.Channels?
Channels can be used to decouple producers and consumers in a publish-and-subscribe scenario, improving performance through parallelism. The producer-consumer pattern ultimately increases the application's throughput, boosting the amount of work done in a given time period.
To get started with a .NET Core Console App in Visual Studio, you need to install the System.Threading.Channels
NuGet package.
You can do this through the NuGet Package Manager in Visual Studio 2022 or by running the following command in the .NET CLI:
dotnet add package System.Threading.Channels
Create a channel in .NET Core
There are two types of channels: bounded channels with a finite capacity and unbounded channels with unlimited capacity. Both types can be created using the Channel static class in the System.Threading.Channels
namespace.
The CreateBounded method creates a channel with a finite number of messages, while CreateUnbounded creates a channel with unlimited capacity, capable of holding an infinite number of messages.
For example, an unbounded channel for string objects can be created as follows:
var channel = Channel.CreateUnbounded<string>();
Bounded channels have a FullMode property that defines the behavior when the channel is full during a write operation. This property controls how the channel behaves in such situations.
- Wait
- DropWrite
- DropNewest
- DropOldest
For example, how to create a bounded channel with a capacity of 1,000 messages and sets the FullMode property to Wait.
Channel.CreateBounded<string>(new BoundedChannelOptions(1000) { FullMode = BoundedChannelFullMode.Wait });
This ensures that when the channel is full, the write operation will wait until space becomes available.
Write data to a channel in .NET Core
To write data to a channel, you can use the WriteAsync
method, as shown in the following code:
await channel.Writer.WriteAsync(".Net Core");
This asynchronously writes the message ".Net Core" to the channel.
Read data from a channel in .NET Core
To read data from a channel, you can use the ReadAsync
method along with the WaitToReadAsync
method to check if there’s data to read.
while (await reader.WaitToReadAsync()) { if (reader.TryRead(out var message)) { Console.WriteLine(message); } }
For example, complete code listing that demonstrates writing and reading data to and from a channel.
class Program { static async Task Main(string[] args) { await SingleProducerSingleConsumer(); Console.ReadKey(); } public static async Task SingleProducerSingleConsumer() { // Create an unbounded channel to hold string messages var channel = Channel.CreateUnbounded<string>(); var reader = channel.Reader; // Producer: Add messages to the channel for (int i = 1; i <= 5; i++) { await channel.Writer.WriteAsync($"Message {i}"); } // Consumer: Read messages from the channel and print them while (await reader.WaitToReadAsync()) { if (reader.TryRead(out var message)) { Console.WriteLine($"Consumed: {message}"); } } } }
In this example:
- Producer: In this example, the producer writes five string messages to the channel, with each message formatted as
"Message 1"
,"Message 2"
, etc. - Consumer: The consumer continuously checks if there are messages to read and, if available, reads each message and prints it to the console.
When executed, this program will sequentially print:
Consumed: Message 1 Consumed: Message 2 Consumed: Message 3 Consumed: Message 4 Consumed: Message 5
There are various ways to implement the producer-consumer pattern, such as using BlockingCollection or TPL Dataflow. However, channels are faster and more efficient than these alternatives.
- How to fix 'Failure sending mail' in C#
- How to Parse a Comma-Separated String from App.config in C#
- How to convert a dictionary to a list in C#
- How to retrieve the Executable Path in C#
- How to validate an IP address in C#
- How to retrieve the Downloads Directory Path in C#
- C# Tutorial
- Dictionary with multiple values per key in C#