Channels the missing piece
Pipelines are the missing piece relative to channels in golang. Channels are actually what make golang tick. Channels are the core concurrency tool. If you're using something like a coroutine in C# but using thread synchronisation primatives (semaphore, monitor, interlocked, etc..) then it's not the same.
Almost the same - Pipelines, but baked in
8 years later, and .Net Standard (.Net Framework / .Net Core) has support for Pipelines [https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/]. Pipelines are preferred for network processing. Aspcore now rates among the top 11 plaintext throughput request rates [https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext].
Microsoft advises best practice for interfacing with network traffic: the awaited network bytes (Completion Port IO) should put data into a pipeline, and another thread should read data from the pipeline asynchronously. Many pipelines can be used in series for various processes on the byte stream. The Pipeline has a reader and a writer cursor, and the virtual buffer size will cause backpressure on the writer to reduce unnecessary use of memory for buffering, typically slowing down network traffic.
There are some critical differences between Pipelines and Go Channels. Pipelines aren't the same as a golang Channel. Pipelines are about passing mutable bytes rather than golang channels which are for signalling with memory references (including pointers). Finally, there is no equivalent select
with Pipelines.
(Pipelines use Spans [https://adamsitnik.com/Span/], which have been around for a little while, but now are optimised deeply in .Net Core. Spans improve performance significantly. The .Net core support improves performance further but only incrementally, so .Net Framework use is perfectly fine. )
So pipelines are a built-in standard should help replace golang channels in .Net, but they are not the same, and there will be plenty of cases where pipelines are not the answer.
Direct Implementations of Golang Channel
You would need to be careful (as with golang) that passed messages via a .Net Channel indicate a change of ownership over an object. This is something only a programmer can track and check, and if you get it wrong, you'll two or more threads accessing data without synchronisation.