3.信号量(Semaphore):控制同时访问共享资源的最大线程数。
3.1介绍
信号量是一种同步机制,可以控制同时访问共享资源的最大线程数。它通过维护一个计数器实现,信号量的计数器最大数代表可同时访问共享资源的线程数,线程调用WaitOne() 方法时,如果信号量计数器大于0,则计数器减1,并允许线程继续执行;若计数器为0,则线程被阻塞并等待其他线程释放信号量。通过调用 Release() 方法,可以增加信号量的计数值,从而允许一个或多个等待中的线程获取信号量并继续执行。
3.2应用
//用信号量限制
#region 信号量的使用
// 创建一个信号量,最大容量为3
private static Semaphore _semaphore = new Semaphore(3, 3);//param1 初始值 param2 最大值
private static void TestSemaphore()
{
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(new ParameterizedThreadStart(ProcessSemaphore));
t.Start(i);
}
Console.ReadKey();
}
private static void ProcessSemaphore(object threadId)
{
Console.WriteLine($"任务 {threadId} 正在等待信号量...");
// 请求信号量
_semaphore.WaitOne();
try
{
Console.WriteLine($"任务 {threadId} 获取了信号量,正在访问资源...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"任务 {threadId} 正在执行第 {i} 次操作...");
Thread.Sleep(100);
}
}
finally
{
// 释放信号量
_semaphore.Release();
Console.WriteLine($"任务 {threadId} 释放了信号量");
}
}
#endregion
4.读写锁(ReaderWriterLockSlim):学习如何在读多写少的情况下提高并发性能。
4.1介绍
在 C# 中,ReaderWriterLockSlim 是一种用于控制对共享资源的并发访问的锁,适用于读多写少的场景。与普通的锁相比,ReaderWriterLockSlim 允许多个线程同时读取共享资源,但在写入时会阻止其他读写操作,从而实现更高的并发性能。
4.2与普通锁对比
#region 普通锁实现竞争资源读写 读多写少
private static int _resource = 0;
private static readonly object _lockResource = new object();
private static long TestLockResource()
{
List<Thread> threads = new List<Thread>();
//记录开始时间
Stopwatch stopwatch = Stopwatch.StartNew();
//开启20个读线程
for (int i = 0; i < 20; i++)
{
var t = new Thread(ReadResource);
t.Start();
threads.Add(t);
}
//开启2个写线程 100ms写一个数据 写1-100
for (int i = 0; i < 2; i++)
{
var t = new Thread(new ParameterizedThreadStart(WriteResource));
t.Start(t.ManagedThreadId);
threads.Add(t);
}
//等待所有线程结束
threads.ForEach(t => t.Join());
//记录结束时间
stopwatch.Stop();
Console.WriteLine($"应用普通总共耗时:{stopwatch.ElapsedMilliseconds} ms.");
return stopwatch.ElapsedMilliseconds;
}
private static void ReadResource()
{
lock (_lockResource)
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine($"当前资源值为:{_resource} 读取次数:{i}");
Thread.Sleep(10);
}
}
}
private static void WriteResource(object tid)
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine($"线程{tid}正在写入资源... 次数:{i}");
lock (_lockResource)
{
_resource = i;
Console.WriteLine($"当前资源值为:{_resource}");
}
Thread.Sleep(10);
}
}
#endregion
缺点:使用普通锁时,读得时候也要阻塞线程,而使用读写锁可以在当前线程没有写得情况下,读线程可以多个同时读,用法如下:
#region 使用读写锁实现资源读写
private static int _resource2 = 0;
private static readonly ReaderWriterLockSlim _lockResource2 = new ReaderWriterLockSlim();
private static long TestLockResource2()
{
List<Thread> threads = new List<Thread>();
//记录开始时间
Stopwatch stopwatch = Stopwatch.StartNew();
//开启20个读线程
for (int i = 0; i < 20; i++)
{
var t = new Thread(ReadResource2);
t.Start();
threads.Add(t);
}
//开启2个写线程 100ms写一个数据 写1-100
for (int i = 0; i < 2; i++)
{
var t = new Thread(new ParameterizedThreadStart(WriteResource2));
t.Start(t.ManagedThreadId);
threads.Add(t);
}
//等待所有线程结束
threads.ForEach(t => t.Join());
//记录结束时间
stopwatch.Stop();
Console.WriteLine($"读写锁总共耗时:{stopwatch.ElapsedMilliseconds} ms.");
return stopwatch.ElapsedMilliseconds;
}
private static void ReadResource2()
{
_lockResource2.EnterReadLock();
try
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine($"当前资源值为:{_resource2} 读取次数:{i}");
Thread.Sleep(10);
}
}
finally
{
_lockResource2.ExitReadLock();
}
}
private static void WriteResource2(object tId)
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine($"线程{tId}正在写入资源... 次数:{i}");
_lockResource2.EnterWriteLock();
try
{
_resource2 = i;
Console.WriteLine($"当前资源值为:{_resource2}");
Thread.Sleep(10);
}
finally
{
_lockResource2.ExitWriteLock();
}
}
}
#endregion
结果如下:

1131

被折叠的 条评论
为什么被折叠?



