Signals in C#

Total: 6 Average: 3.3


Currently, the thread synchronization in С# causes some difficulties, in particular, when passing synchronization primitives between the objects of your application and supporting them in the future.

The current model with Task and IAsyncResult, as well as with TPL, solve all issues through a proper design. However, I would like to create a simple class that allows sending and receiving signals with a thread lock.

The interface is as follows:

Where T is an entity to be passed to a receiver.

Here is the example of the call:

To receive a signal of the object, we will create a factory:

Signal is an internal class to synchronize within a single process. A reference to the object is required for synchronization.

CrossProcessSignal is an internal class that can synchronize threads in separate processes.

Signal Implementation

The first thing that comes to my mind is to block the thread execution in Receive using Semaphore and  to call Release() of this semaphore with the amount of blocked threads in the Send method. After unlocking threads, it is necessary to return the result from the field of the T buffer class. However, we do not know of  how many threads will hang in Receive, thus, we cannot guarantee that another couple of threads will not be added to the Release call.

We chose AutoResetEvent as a synchronization primitive. For each new thread, we will create its custom AutoResetEvent and store it in Dictionary<int,AutoResetEvent>, where the key is the thread ID.

The class fields look like this:

We will need to use the sync object when calling Send so that several threads do not overwrite the buffer.

The isDisposabled flag defines whether Dispose() was called. If not called, then we call it in the destructor.

Now, let’s talk about the Receive method.

GetEvents() retrieves AutoResetEvent, if any, from the dictionary. If not, then it creates a new one and puts it into the dictionary.

waiter.WaitOne() is a thread lockup before the signal waiting.

waiter.Reset() resets the current state of AutoResetEvent. The next WaitOne call will lock up the thread.

Now, we need to call the Set method for each AutoResetEvent.

We can test this model using the following script:

This implementation requires a lot of rework in terms of reliability. The source code includes an inter-process implementation of this idea with passing the signal through shared memory.

Sources on GitHub

Kirill Kovalenko

For more than 5 years, Kirill has been developing bank systems in C#
Kirill Kovalenko

Latest posts by Kirill Kovalenko (see all)