<center><h1>.Net SemaphoreSlim & Lock</h1></center>
### Introduction
Locks are widely used in multi-thread programming for thread safety, which aims to ensure the data or objects will be modified correctly by threads.
But locks can't be used in async functions because async functions might not be done by the same thread from the start through the end, so C# provides Semaphore to do that in async use cases.
### Lock
Locks are effective for thread safety during multithreaded programming, simple examples are provided for easy understanding.
The object intended to be locked can be sepcify with lock(object).
w/o lock:
```csharp
var sum = 0;
void DoAdd()
{
for(int i = 0; i < 10; i++)
{
sum++;
Console.Write($"{sum} ");
}
}
var t1 = new Thread(DoAdd);
var t2 = new Thread(DoAdd);
t1.Start();
t2.Start();
Console.ReadLine();
```
Result:

w/ lock:
```csharp
var sum = 0;
var _obj = new Object();
void DoAdd()
{
lock (_obj)
{
for (int i = 0; i < 10; i++)
{
sum++;
Console.Write($"{sum} ");
}
}
}
var t1 = new Thread(DoAdd);
var t2 = new Thread(DoAdd);
t1.Start();
t2.Start();
Console.ReadLine();
```
Reuslt:

Above results show that clearly, locks can ensure the data correctness.
### SemaphoreSlim
But locks can't be used in async functions since the task might be completed by more than one threads, so that C# provided us Semaphore and SemaphoreSlim to do that.
You can imagine that Semaphore is a car park, and has a limited number of cars can be parked:
1. No cars can be entered when the car park is entire.
2. When a car leaves, open a space (release).
3. When a car enters, keep a space for that car (wait/wait async).
Unlike Semaphore, the maxCount is not forced to be set in the constructor.
```csharp
public SemaphoreSlim(int initialCount);
public SemaphoreSlim(int initialCount, int maxCount);
```
The initialCount indicates how many threads can enter in default, call Release() to add one, and call Wait() or WaitAsync() to minus one.
The maxCount indicates the max number of threads can enter.
```csharp
Lock.WaitAsync();
Lock.Release();
```
Examples will be given with SemaphoreSlime as well, which is a light version of Semaphore, and is easier to use o( ̄▽ ̄)d
w/o SemaphoreSlim:
```csharp
var sum = 0;
async Task DoAdding()
{
await Task.Run(() => { sum++; });
}
await Task.WhenAll(Enumerable.Range(0, 5000).Select(_ => DoAdding()));
Console.WriteLine($"Sum: {sum}");
Console.ReadLine();
```
Results:

w/ SemaphoreSlim:
```csharp
var sum = 0;
var Lock = new SemaphoreSlim(1);
async Task DoAdding()
{
await Lock.WaitAsync();
await Task.Run(() => { sum++; });
Lock.Release();
}
await Task.WhenAll(Enumerable.Range(0, 5000).Select(_ => DoAdding()));
Console.WriteLine($"Sum: {sum}");
Console.ReadLine();
```
Result:

### References
https://en.wikipedia.org/wiki/Semaphore_(programming)
https://learn.microsoft.com/en-us/dotnet/api/system.threading.semaphoreslim?view=net-6.0
https://xinyuehtx.github.io/post/%E4%BD%BF%E7%94%A8SemaphoreSlim%E5%AE%9E%E7%8E%B0%E5%BC%82%E6%AD%A5%E7%AD%89%E5%BE%85.html
###### tags: `C#` `.Net` `Lock` `SemaphoreSlim`