Last active
May 11, 2017 06:00
-
-
Save Porges/fcd638790e6bc6365d3656e09d3d7672 to your computer and use it in GitHub Desktop.
Go channel implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| void Main() | |
| { | |
| Channels(); | |
| BufferedChannels(); | |
| ChannelDirections(); | |
| } | |
| // Example from: https://gobyexample.com/channels | |
| void Channels() | |
| { | |
| var c = new Chan<string>(); | |
| Task.Run(() => c.Put("ping")); | |
| var msg = c.Get(); | |
| Console.WriteLine(msg); | |
| } | |
| // Example from: https://gobyexample.com/channel-buffering | |
| void BufferedChannels() | |
| { | |
| var c = new BufferedChan<string>(2); | |
| c.Put("buffered"); | |
| c.Put("channel"); | |
| Console.WriteLine(c.Get()); | |
| Console.WriteLine(c.Get()); | |
| } | |
| // Example from: https://gobyexample.com/channel-directions | |
| void ChannelDirections() | |
| { | |
| void Ping(IWriteChan<string> pings, string message) | |
| => pings.Put(message); | |
| void Pong(IReadChan<string> pings, IWriteChan<string> pongs) | |
| => pongs.Put(pings.Get()); | |
| var thePings = new BufferedChan<string>(1); | |
| var thePongs = new BufferedChan<string>(1); | |
| Ping(thePings, "passed message"); | |
| Pong(thePings, thePongs); | |
| Console.WriteLine(thePongs.Get()); | |
| } | |
| /// A Chan is a bit like an MVar, | |
| /// except that the writer is *also* blocked | |
| /// until a reader is present. | |
| class Chan<T> : IReadChan<T>, IWriteChan<T> | |
| { | |
| enum State { Empty, ReaderWaiting, ValueWritten }; | |
| object _lock = new object(); | |
| State _state = State.Empty; | |
| T _value; | |
| public void Put(T value) | |
| { | |
| lock (_lock) | |
| { | |
| // wait for reader to appear | |
| while (_state != State.ReaderWaiting) | |
| { | |
| Monitor.Wait(_lock); | |
| } | |
| // write value | |
| _value = value; | |
| _state = State.ValueWritten; | |
| Monitor.PulseAll(_lock); | |
| } | |
| } | |
| public T Get() | |
| { | |
| lock (_lock) | |
| { | |
| // wait for empty | |
| while (_state != State.Empty) | |
| { | |
| Monitor.Wait(_lock); | |
| } | |
| // ask writer to write | |
| _state = State.ReaderWaiting; | |
| Monitor.PulseAll(_lock); | |
| // wait for written | |
| do | |
| { | |
| Monitor.Wait(_lock); | |
| } while (_state != State.ValueWritten); | |
| // read value, reset state | |
| var ret = _value; | |
| _value = default(T); | |
| _state = State.Empty; | |
| Monitor.PulseAll(_lock); | |
| return ret; | |
| } | |
| } | |
| } | |
| sealed class BufferedChan<T> : IDisposable, IReadChan<T>, IWriteChan<T> | |
| { | |
| BlockingCollection<T> _queue; | |
| public BufferedChan(int bufferSize) | |
| { | |
| _queue = new BlockingCollection<T>(bufferSize); | |
| } | |
| public void Dispose() => _queue.Dispose(); | |
| public void Put(T value) => _queue.Add(value); | |
| public T Get() => _queue.Take(); | |
| } | |
| interface IReadChan<out T> | |
| { | |
| T Get(); | |
| } | |
| interface IWriteChan<in T> | |
| { | |
| void Put(T value); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment