c#中lock的经典示例
lock 是 C# 中的一种用于同步线程执行的机制,它帮助确保多个线程在访问共享资源时不会发生冲突或数据损坏。其作用是通过给临界区(即多线程访问共享资源的代码段)加锁,使得在同一时刻只能有一个线程进入执行该代码段。
1、lock 的工作原理
- 加锁和解锁:当一个线程进入一个 lock 块时,它会获取一个对象的锁(通常是该块代码所属对象的锁)。其他线程在该锁被释放之前无法进入同一 lock 块。当线程退出 lock 块时,它会自动释放锁。
- 防止线程冲突:lock 主要用于保护共享数据,避免多个线程同时访问或修改共享资源,造成数据不一致或程序崩溃的问题。
2、语法
lock (someObject)
{
// 临界区代码:只有获得锁的线程能进入
// 共享资源的操作代码
}
3、关键点
- someObject:lock 语句后面必须跟一个对象,这个对象用于在多个线程之间进行同步。通常使用类的实例对象,或是使用 this 来加锁当前对象。someObject 作为锁,通常不会使用原始类型数据或者是可变的对象。
- 自动解锁:一旦线程执行完 lock 块中的代码,锁就会自动释放,其他线程就有机会获取锁并进入临界区。
- 死锁:如果两个或多个线程相互等待对方释放锁,可能会导致死锁(即程序进入永久等待状态)。因此,设计时要注意锁的获取顺序和策略,避免这种情况。
4、lock 的实际应用
lock 经常用于以下场景:
- 操作共享数据:当多个线程需要操作同一资源时,通过 lock 来确保只有一个线程能够访问该资源,从而避免数据竞争。
- 数据库或文件访问:当多个线程需要访问数据库或文件时,需要同步这些访问操作,以防止数据损坏。
5、lock的经典示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace Demo
{
class Program
{
int num = 10;//设置当前总票数
void Ticket()
{
while (true)//设置无限循环
{
Console.WriteLine(" " + Thread.CurrentThread.Name + " lock前");//申请lock
lock (this)//锁定代码块,以便线程同步
{
Console.WriteLine(" " + Thread.CurrentThread.Name + " lock中");//获得lock
if (num > 0)//判断当前票数是否大于0
{
Console.WriteLine(" " + Thread.CurrentThread.Name + " Sleep(100)");
Thread.Sleep(100);//使当前线程休眠100毫秒
Console.WriteLine(" " + Thread.CurrentThread.Name + "----票数" + num--);//票数减1
}
else
{
Console.ReadLine();
}
}
Console.WriteLine(" " + Thread.CurrentThread.Name + " lock后");//释放lock
}
}
static void Main(string[] args)
{
Program p = new Program();//创建对象,以便调用对象方法
Thread tA = new Thread(new ThreadStart(p.Ticket));//分别实例化4个线程,并设置名称
tA.Name = "线程A";
Thread tB = new Thread(new ThreadStart(p.Ticket));
tB.Name = "线程B";
Thread tC = new Thread(new ThreadStart(p.Ticket));
tC.Name = "线程C";
Thread tD = new Thread(new ThreadStart(p.Ticket));
tD.Name = "线程D";
tA.Start(); //分别启动线程
Console.WriteLine("tA.Start");
tB.Start();
Console.WriteLine("tB.Start");
tC.Start();
Console.WriteLine("tC.Start");
tD.Start();
Console.WriteLine("tD.Start");
Console.ReadLine();
}
}
}
运行结果
tA.Start ->main中,线程A启动
线程A lock前 -> 进入线程A,线程A申请lock
线程A lock中 -> 线程A获得lock
线程A Sleep(100) -> 线程A休眠
tB.Start ->main中,线程B启动 ->这里可以看到线程休眠程序,会返回main
线程B lock前 -> 进入线程B,线程B申请lock
tC.Start ->main中,线程C启动
线程C lock前 -> 进入线程C,线程C申请lock
tD.Start ->main中,线程D启动
线程D lock前 -> 进入线程D,线程D申请lock
线程A----票数10 -> 线程A休眠结束,线程A运行
线程A lock后 -> 线程A释放lock
线程A lock前 -> 线程A申请lock
线程B lock中 -> 线程B获得lock
线程B Sleep(100) -> 线程B休眠
线程B----票数9 -> 线程B休眠结束,线程B运行 ->这里可以看到线程休眠,不会释放lock
线程B lock后 -> 线程B释放lock
线程B lock前 -> 线程B申请lock
线程C lock中 -> 线程C获得lock
线程C Sleep(100) -> 线程C休眠
线程C----票数8 -> 线程C休眠结束,线程C运行
线程C lock后 -> 线程C释放lock
线程C lock前 -> 线程C申请lock
线程D lock中
线程D Sleep(100)
线程D----票数7
线程D lock后
线程D lock前
线程A lock中
线程A Sleep(100)
线程A----票数6
线程A lock后
线程A lock前
线程B lock中
线程B Sleep(100)
线程B----票数5
线程B lock后
线程B lock前
线程C lock中
线程C Sleep(100)
线程C----票数4
线程C lock后
线程C lock前
线程D lock中
线程D Sleep(100)
线程D----票数3
线程A lock中
线程A Sleep(100)
线程D lock后
线程D lock前
线程A----票数2
线程A lock后
线程A lock前
线程B lock中
线程B Sleep(100)
线程B----票数1
线程B lock后
线程B lock前
线程C lock中
6、 死锁的经典示例
using System;
using System.Threading;
class Program
{
private readonly object lock1 = new object();
private readonly object lock2 = new object();
public void Thread1()
{
lock (lock1)
{
Console.WriteLine("Thread 1: Acquired lock1");
Thread.Sleep(1000); // 模拟一些工作
Console.WriteLine("Thread 1: Trying to acquire lock2");
lock (lock2)
{
Console.WriteLine("Thread 1: Acquired lock2");
}
}
}
public void Thread2()
{
lock (lock2)
{
Console.WriteLine("Thread 2: Acquired lock2");
Thread.Sleep(1000); // 模拟一些工作
Console.WriteLine("Thread 2: Trying to acquire lock1");
lock (lock1)
{
Console.WriteLine("Thread 2: Acquired lock1");
}
}
}
static void Main(string[] args)
{
Program p = new Program();//创建对象,以便调用对象方法
// 创建两个线程
Thread t1 = new Thread(p.Thread1);
Thread t2 = new Thread(p.Thread2);
// 启动线程
t1.Start();
t2.Start();
// 等待线程结束
t1.Join();
t2.Join();
}
}
运行结果
Thread 1: Acquired lock1
Thread 2: Acquired lock2
Thread 1: Trying to acquire lock2
Thread 2: Trying to acquire lock1