C#中的Lock语句将Lock中的语句块视为临界区,让多线程访问临界区代码时,必须顺序访问,它的作用是在多线程环境下,确保临界区中的对象只被一个线程操作,防止出现对象被多次改变的情况。lock属于互斥锁,在编译时此语句通过System.Threading.Monitor.EnterSystem.Threading.Monitor.Exit实现线程锁。

注意的地方是,lock参数对象必须是一个不可变对象,否则无法阻止另一个线程进入临界区,常见的传入参数多用private static readonly或者private static修饰。

lock 语句 - C# 参考 | Microsoft Learn

Lock锁演示

创建一个控制台应用程序,该应用程序使用lock关键字来控制奇数线程和偶数线程的打印,要求首先执行偶数线程,然后执行奇数线程。 根据题目要求,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System;
using System.Text;
using System.Threading;

namespace CRApp
{
class Program
{
private static readonly object locker = new object();

static int Main(string[] args)
{
Thread t1 = new Thread(PrintEvenNums);
Thread t2 = new Thread(PrintOddNums);
t1.Start();
t2.Start();
Console.ReadKey();
return (0);
}

public static void PrintEvenNums() // 打印0~10之间的偶数
{
lock (locker)
{
for (int i = 0; i < 10; i++)
{
if (i % 2 == 0)
{
Console.WriteLine(i);
}
Thread.Sleep(10);
}
}
}

public static void PrintOddNums () // 打印0~10之间的奇数
{
lock (locker)
{
for (int i = 0; i < 10; i++)
{
if (i % 2 != 0)
{
Console.WriteLine(i);
}
Thread.Sleep(10);
}
}
}
}
}

运行该程序,结果如下: 0 2 4 6 8 1 3 5 7 9

从上面的运行效果可以看出,奇数线程只有在偶数线程完全打印后才执行,每次运行程序打印的效果都是一样的。

在单例模式中使用lock

多线程服务器中使用单例模式,并且使用懒汉模式,需要确保线程安全,多使用lock语句

伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
private static readonly locker = new object();

//或者这样写也可以。

//private static locker = new object();

lock(locker)
{
if(.. == null)
{
...
}
}