线程同步
1、Lock
static void Main(string[] args)
{
Console.WriteLine("*********LockShow*********");
for (int i = 0; i < 100; i++)
{
int k = i;
ThreadPool.QueueUserWorkItem((t) =>
{
LockShow(k);
});
}
Console.ReadKey();
}
private static readonly object _objLock = new object();
static void LockShow(int num)
{
Console.WriteLine($"LockShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} Enter");
lock (_objLock)//阻塞线程
{
Console.WriteLine($"LockShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 开始");
Thread.Sleep(1000);
Console.WriteLine($"LockShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 结束");
}
}
output:
2、Mutex 互斥锁
可跨多个线程
在这里插入代码片
static void Main(string[] args)
{
Console.WriteLine("*********MutexShow*********");
for (int i = 0; i < 50; i++)
{
int k = i;
ThreadPool.QueueUserWorkItem((t) =>
{
MutexShow(k);
});
}
Console.ReadKey();
}
/// <summary>
/// 是否创建新的互斥锁,false:锁已经存在,true:获取锁成功
/// </summary>
private static readonly bool createNew;
/// <summary>
/// initiallyOwned=false 未拥有锁
/// </summary>
private static Mutex mutex = new Mutex(false, "MutexShow",out createNew);
static void MutexShow(int num)
{
Console.WriteLine($"MutexShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} Enter");
bool firstInstance;
try
{
//mutex.WaitOne(100);//指定100毫秒时间间隔,若在这段时间内没有接收到信号则跳过等待继续执行
if (mutex.WaitOne())//获取锁,阻止当前线程,直到当前 WaitHandle 收到信号。
{
try
{
Console.WriteLine(createNew);
Console.WriteLine($"MutexShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 开始");
Thread.Sleep(200);
Console.WriteLine($"MutexShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 结束");
}
finally
{
//if (!mutex.SafeWaitHandle.IsClosed)
mutex.ReleaseMutex();//释放锁
}
}
else
{
Console.WriteLine($"MutexShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 等待超时");
}
}
finally
{
//mutex.Dispose();
}
}
output:
Mutex扩展:
使用Mutex实现客户端只能登录一次
/// <summary>
/// 互斥name为空,则为局部Mutex,无法用于进程间的同步
/// </summary>
private static Mutex mutex = new Mutex(false, "ConsoleApp1", out createdNew);
static void Main(string[] args)
{
if (createdNew)
{
Console.WriteLine("程序正在运行中");
}
else
{
Console.WriteLine("程序只能登录一次");
}
Console.ReadKey();
}
3、Monitor
lock关键字基于Monitor.Enter,Monitor.Exit 实现。lock能实现的Monitor都能实现,Monitor能做的lock却不一定。
Monitor.TryEnter可以避免死锁,lock不能做到
static void Main(string[] args)
{
Console.WriteLine("*********MonitorShow*********");
for (int i = 0; i < 50; i++)
{
int k = i;
ThreadPool.QueueUserWorkItem((t) =>
{
MonitorShow(k);
});
}
Console.ReadKey();
}
private static object objLock2 = new object();
static void MonitorShow(int num)
{
Console.WriteLine($"MonitorShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} Enter");
try
{
//Monitor.TryEnter(objLock2, 1000);//参数二:等待锁时间,超过1000毫秒就不在等待获取锁。TryEnter不阻塞线程,不会造成死锁
//Monitor.TryEnter(objLock2, -1);//等同于Monitor.Enter(objLock2)
Monitor.Enter(objLock2);//阻塞线程
Console.WriteLine($"MonitorShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 开始");
Thread.Sleep(1000);
Console.WriteLine($"MonitorShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 结束");
}
finally
{
Monitor.Exit(objLock2);
}
}
output:
4、Semaphore 信号量
static void Main(string[] args)
{
Console.WriteLine("*********SemaphoreShow*********");
for (int i = 0; i < 50; i++)
{
int k = i;
ThreadPool.QueueUserWorkItem((t) =>
{
SemaphoreShow(k);
});
}
Console.ReadKey();
}
/// <summary>
/// Semaphore信号量:
/// 用于控制同时刻访问资源的最大线程数
/// 参数:
/// initialCount:当前可用线程数量,为0代表当前无可用线程
/// maximumCount:最大可用线程数量
/// </summary>
private static Semaphore sema = new Semaphore(1, 1);
static void SemaphoreShow(int num)
{
Console.WriteLine($"SemaphoreShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} Enter");
sema.WaitOne();//阻塞当前线程,直到当前线程的WaitHandle 收到信号,initialCount数量-1
//sema.WaitOne(100);//指定100毫秒时间间隔,若在这段时间内没有接收到信号则跳过等待继续执行
Console.WriteLine($"SemaphoreShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 开始");
Thread.Sleep(2000);
Console.WriteLine($"SemaphoreShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 结束");
sema.Release();//=sema.Release(1),initialCount数量+1
//sema.Release(1);
}
output:
5、AutoResetEvent
static void Main(string[] args)
{
Console.WriteLine("*********AutoResetEventShow*********");
for (int i = 0; i < 50; i++)
{
int k = i;
ThreadPool.QueueUserWorkItem((t) =>
{
AutoResetEventShow(k);
});
}
Console.ReadKey();
}
/// <summary>
/// initialState=true 不阻塞,
/// initialState=false 阻塞
/// </summary>
private static AutoResetEvent are = new AutoResetEvent(true);
static void AutoResetEventShow(int num)
{
are.WaitOne();//WaitOne默认调用Reset方法
//are.Reset();
Console.WriteLine($"AutoResetEventShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} Enter");
Console.WriteLine($"AutoResetEventShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 开始");
Thread.Sleep(2000);
Console.WriteLine($"AutoResetEventShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 结束");
are.Set();//initialState=true,不阻塞
}
output:
6、ManualResetEvent
跟 AutoResetEvent 很相似,不同点在于,AutoResetEvent 的调用WaitOne()方法时会默认调用Reset()方法,每次只有一个线程,其他线程进入等待。而ManualResetEvent一旦调用Set(),其他所有等待的线程都会不阻塞
static void Main(string[] args)
{
Console.WriteLine("*********ManualResetEventShow*********");
for (int i = 0; i < 50; i++)
{
int k = i;
ThreadPool.QueueUserWorkItem((t) =>
{
ManualResetEventShow(k);
});
}
mre.Set();//initialState=true,使所有后续等待的线程不阻塞
Console.ReadKey();
}
/// <summary>
/// initialState=true 不阻塞,
/// initialState=false 阻塞
/// </summary>
private static ManualResetEvent mre = new ManualResetEvent(true);
static void ManualResetEventShow(int num)
{
mre.WaitOne();
//mre.Reset();//阻塞,使后续线程进入阻塞
Console.WriteLine($"ManualResetEventShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} Enter");
Console.WriteLine($"ManualResetEventShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 开始");
Thread.Sleep(2000);
Console.WriteLine($"ManualResetEventShow 线程ID:{Thread.CurrentThread.ManagedThreadId} 编号:{num} 结束");
//mre.Set();//initialState=true,使所有后续等待的线程不阻塞
}
output: