一、lock的作用
Lock可以看成在操作系统中的临界区,Lock区域内的代码表示临界区,使得同一时间只有一个线程能够进入Lock所包含的函数中,实现原子操作,保护同一资源只有一个线程进行修改,实现不同线程中数据的同步。
未进入Lock的线程将被阻塞等待,直到Lock锁被打开才唤醒其中一个进入,并且进行上锁
(总的来说Lock在多线程的运行中可以保证数据安全,对于保护的区域只允许一个线程使用!)
二、lock的基础使用
private Object thisLock = new Object();//创建对象锁
或者
private static readonly object thisLock = new object();
lock (thisLock)
{
// Critical code section
}
注意事项:
- 需要注意的是首先创建的对象锁,应该是不同线程能够访问的同一个对象,因此至少应该是在类中是全局的,不应为局部变量。
- 为了实现全局的对象锁,可以使用static,例:private static Object thisLock = new Object();//创建对象锁
- 结合自己线程的运行特性,选择正确的对象锁
三、lock(this)的用法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Lock测试
{
class Program
{
static void Main(string[] args)
{
C1 c1 = new C1();
//在t1线程中调用LockMe,并将deadlock设为true(将出现死锁)
Thread t1 = new Thread(c1.LockMe);
t1.Start(true);
Thread.Sleep(100);
//在主线程中lock c1
lock (c1)
{
//调用没有被lock的方法
c1.DoNotLockMe();
//调用被lock的方法,并试图将deadlock解除
c1.LockMe(false);
}
Console.Read();
}
}
class C1
{
private bool deadlocked = true;
//这个方法用到了lock,我们希望lock的代码在同一时刻只能由一个线程访问
public void LockMe(object o)
{
lock (this)
{
while (deadlocked)
{
deadlocked = (bool)o;
Console.WriteLine("Foo: I am locked :("+o);
Thread.Sleep(500);
}
}
}
//所有线程都可以同时访问的方法
public void DoNotLockMe()
{
Console.WriteLine("I am not locked :)");
}
}
}
在这个代码中,出现了两个lock锁,在C1中有一个锁,lock(this)主要是对LockMe方法进行上锁,在主要线程中又有一个Lock锁,主要是Lock(c1),该代码中,lock(this)其实就是把C1的实例c1当钥匙拿走,导致主线程后的lock(c1)无法继续执行。
当我们将lock的钥匙修改,如下:
private bool deadlocked = true;
private object locker = new object();
//这个方法用到了lock,我们希望lock的代码在同一时刻只能由一个线程访问
public void LockMe(object o)
{
lock (locker)
{
while (deadlocked)
{
deadlocked = (bool)o;
Console.WriteLine("Foo: I am locked :("+o);
Thread.Sleep(500);
}
}
}
//所有线程都可以同时访问的方法
public void DoNotLockMe()
{
Console.WriteLine("I am not locked :)");
}
}
}
我们可以发现,就可以继续执行
总结:
1. lock(this)的缺点就是在一个线程(例如本例的t1)通过执行该类的某个使用"lock(this)"的方法(例如本例的LockMe())锁定某对象之后, 导致整个对象无法被其他线程(例如本例的主线程)访问 - 因为很多人在其他线程(例如本例的主线程)中使用该类的时候会使用类似lock(c1)的代码。
2. 锁定的不仅仅是lock段里的代码,锁本身也是线程安全的。
3. 我们应该使用不影响其他操作的私有对象作为locker。
4. 在使用lock的时候,被lock的对象(locker)一定要是引用类型的,如果是值类型,将导致每次lock的时候都会将该对象装箱为一个新的引用对象(事实上如果使用值类型,C#编译器(3.5.30729.1)在编译时就会给出一个错误)。
参考文献:
关于lock(this)的说明及用法-CSDN博客
C# 关于线程锁lock的使用方法_c# lock方法-CSDN博客
C#线程锁(Lock)_c# lock_月光在发光的博客-CSDN博客