The motivation
started from coming into contact with the multi-thread (process) programming model, and what I learned was the synchronization primitives related to the semaphore (Semaphore). I don’t know why there is no corresponding stuff in .Net Framework. The terrible thing is that I used her to implement a lot of time-tested C++ codes. In order to prevent the revolutionary martyrs from taking the medicine and bleeding in vain, I had to give birth to one myself.
What is a semaphore?
If you already understand the concept of a semaphore, please skip this section.
Semaphore is a facility used in a multi-threaded environment. It is responsible for coordinating various threads to ensure that they can use public resources correctly and reasonably.
Let's take a look at how a parking lot works. For the sake of simplicity, assume that the parking lot has only three parking spaces, and all three parking spaces are empty at the beginning. This is if five cars come at the same time, and the gatekeeper allows three of them to enter without hindrance, and then puts down the car block, the remaining cars must wait at the entrance, and subsequent cars will also have to wait at the entrance. At this time, a car left the parking lot. After the gatekeeper learned about it, he opened the car barrier and put one car in. If two more cars left, he could put two more cars in, and so on.
In this parking lot system, parking spaces are public resources, each car is like a thread, and the gatekeeper acts as a semaphore.
Furthermore, the characteristics of the semaphore are as follows: the semaphore is a non-negative integer (the number of parking lots), and all threads (vehicles) passing it will reduce the integer by one (passing it is of course to use resources). When the integer value is At zero, all threads trying to pass it will be in a waiting state. We define two operations on the semaphore: Wait and Release. When a thread calls the Wait operation, it either passes and then decrements the semaphore by one, or it waits until the semaphore is greater than one or times out. Release actually performs an add operation on the semaphore, which corresponds to the vehicle leaving the parking lot. The reason why this operation is called "release" is that the add operation actually releases the resources guarded by the semaphore.
accomplish
As we all know, the thread synchronization facilities provided in the .Net Framework class library include:
Monitor, AutoResetEvent, ManualResetEvent, Mutex, ReadWriteLock and InterLock. Among them, AutoResetEvent, ManualResetEvent, and Mutex are derived from WaitHandler, and they actually encapsulate the kernel objects provided by the operating system. Others should be native to the .Net virtual machine. Obviously facilities from operating system kernel objects are less efficient to use. However, efficiency is not an issue we have to consider here. We will use two Monitors and a ManualResetEvent object to simulate a semaphore.
The code is as follows:
public class Semaphore
{
private ManualResetEvent waitEvent = new ManualResetEvent(false);
private object syncObjWait = new object();
private int maxCount = 1; //Maximum number of resources
private int currentCount = 0; //The current number of resources
public Semaphore()
{
}
public Semaphore( int maxCount )
{
this.maxCount = maxCount;
}
public bool Wait()
{
lock( syncObjWait ) //Only one thread can enter the following code
{
bool waitResult = this.waitEvent.WaitOne(); //The number of resources waiting here is greater than zero
if(waitResult)
{
lock(this)
{
if( currentCount > 0 )
{
currentCount--;
if( currentCount == 0 )
{
this.waitEvent.Reset();
}
}
else
{
System.Diagnostics.Debug.Assert( false, "Semaphore is not allow current count < 0" );
}
}
}
return waitResult;
}
}
/**//// <summary>
/// Wait operation that allows timeout return
/// </summary>
/// <param name="millisecondsTimeout"></param>
/// <returns></returns>
public bool Wait(int millisecondsTimeout)
{
lock( syncObjWait ) // Monitor ensures that the scope class code is within the critical section
{
bool waitResult = this.waitEvent.WaitOne(millisecondsTimeout,false);
if(waitResult)
{
lock(this)
{
if( currentCount > 0 )
{
currentCount--;
if( currentCount == 0 )
{
this.waitEvent.Reset();
}
}
else
{
System.Diagnostics.Debug.Assert( false, "Semaphore is not allow current count < 0" );
}
}
}
return waitResult;
}
}
public bool Release()
{
lock( this ) // Monitor ensures that the scope class code is within the critical section
{
currentCount++;
if( currentCount > this.maxCount )
{
currentCount = this.maxCount;
return false;
}
this.waitEvent.Set(); //Allow the thread calling Wait to enter
}
return true;
}
}