【C#多线程】lock/Monitor/Interlocked
1.lock
lock 用于读一个引用类型进行加锁,同一时刻内只有一个线程能够访问此对象。lock 是Monitor的语法糖。
Lock 锁定的对象,应该是静态的引用类型(字符串除外)。
class Program
{
private static object obj = new object();
private static int sum = 0;
static void Main(string[] args)
{
Thread thread1 = new Thread(Sum1);
thread1.Start();
Thread thread2 = new Thread(Sum2);
thread2.Start();
while (true)
{
Console.WriteLine($"{DateTime.Now.ToString()}:" + sum);
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
public static void Sum1()
{
sum = 0;
lock (obj)
{
for (int i = 0; i < 10; i++)
{
sum += i;
Console.WriteLine("Sum1");
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}
}
public static void Sum2()
{
sum = 0;
lock (obj)
{
for (int i = 0; i < 10; i++)
{
sum += 1;
Console.WriteLine("Sum2");
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}
}
}
类的内部还可以调用自身实例作为锁对象:
class A
{
lock(this){}
}
当锁定时,其它线程必须等待锁中的线程执行完成并释放锁。但是这可能会给程序带来性能影响。
锁不太适合I/O场景,例如文件I/O,繁杂的计算或者操作比较持久的过程,会给程序带来很大的性能损失。
2. Monitor
Monotor 是一个静态类型。
- Wait执行后,阻塞当前线程,进入等待队列,obj上的锁被释放。
Wait(Object); //释放obj上的锁,当前线程进入等待队列(阻塞)
Wait(Object, Int32); //释放obj上的锁,当前线程进入等待队列,并在一定时间后进入就绪队列
- Pulse/PulseAll执行后,有一个/所有在等待队列中的线程将进入就绪队列。(队列先进先出,所以最先进入的线程将被移入就绪队列)
private static object obj = new object();
private static bool acquiredLock = false;
public static void Test()
{
try
{
Monitor.Enter(obj, ref acquiredLock);
}
catch { }
finally
{
if (acquiredLock)
Monitor.Exit(obj);
}
}
3.Interlocked
1.前言
轻量级同步,因为它仅对整形数据(即int类型,long也行)进行同步。为多个线程共同访问的变量提供原子操作,这个类是一个静态类 它提供了以线程安全的方式递增、递减、交换和读取值的方法。
效率高于lock,但只能解决简单的同步问题:自增、自减、加、读取、赋值
2.方法
https://blog.51cto.com/13713878410/1530358
这些方法都是线程安全的。
public static uint Exchange (ref uint location1, uint value);
//将value赋值给location1
//返回值:location1的原始值
public static float CompareExchange (ref float location1, float value, float comparand);
//location1与comparand比较,若相等,则将location1置value
//返回值location1的原始值
private void ExchangeValue4()
{
//if value4=0, set value4=99
Interlocked.CompareExchange(ref value4, 99,0);
}