线程锁

一:锁机制

1.net锁机制

时间锁、信号锁、互斥锁、读写锁、互锁,易变构造

分类:

  1. 用户模式锁:通过一些cpu指令或者一个死循环达到thread等待和休眠。
  2. 内核模式锁:调用win32底层的代码,来实现thread的各种操作,如:Thread.Sleep
  3. 混合锁:用户模式+内核模式

2.为什么要用锁?

多线程对一个共享资源进行操作的时候,容易出现共享资源混乱的问题

二:用户模式锁

  1. 易变结构:一个线程读,一个线程写,z在release版本中会有bug;

解决问题:

  • Thread.MemoryBarrier, Thread.VolatileRead

  • 关键字:volatile

    1. 不可以底层对代码进行优化

    2. 我的Read和Write都是从memrory中读取,读取的数据是最新的。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      public static volatile bool isStop = false;
      static void Main(string[] args)
      {
      var t = new Thread(()=> {
      var isSuccess = false;
      while (!isStop)
      {
      isSuccess = true;
      }
      });
      t.Start();
      Thread.Sleep(1000);
      isStop = true;
      t.Join();
      Console.WriteLine("主线程执行结束!");
      Console.ReadKey();
      }

2.互锁结构:Interlocked[还只能做一些简单类型计算]

Interlocked:

  • Increment:自增

    var sum = 0;
        for (int i = 0; i < 10; i++)
        {
            Interlocked.Increment(ref sum);
        }
         //最后sum=10,自增单位为1
  • Decrement:自减

    1
    2
    3
    4
    5
    6
    var sum = 0;
    for (int i = 0; i < 10; i++)
    {
    Interlocked.Decrement(ref sum);
    }
    //最后sum=-10,自减单位为1
  • Exchange:赋值

    1
    2
    3
    var sum = 5;
    Interlocked.Exchange(ref sum,10);
    //最好sum=10;
  • Add:增加指定的值

    1
    2
    3
    var sum = 5;
    Interlocked.Add(ref sum,2);
    //最好sum=7
  • CompareExchange:比较赋值

    var sum = 5;
    Interlocked.CompareExchange(ref sum, 6, 5);
    //如果sum==5,true:sum=6,false=5;

    3.旋转锁:Soinlock

特殊的业务逻辑让thread在用户模式下进行自选,欺骗cpu当前thread正在运行中

用户模式—>内核模式—>用户模式

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
public static SpinLock spinLock = new SpinLock();
static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
var task = Task.Factory.StartNew(()=> {
Run();
});
}
Console.ReadKey();
}
static void Run()
{
for (int i = 0; i < 100; i++)
{
try
{
var b = false;
spinLock.Enter(ref b); ;
num++;
Console.WriteLine(num);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
spinLock.Exit();
}
}
}

1573279748631

SpinLock 仅当您确定这样做可以改进应用程序的性能之后才能使用。另外,务必请注意 SpinLock 是一个值类型(出于性能原因)。因此,您必须非常小心,不要意外复制了 SpinLock 实例,因为两个实例(原件和副本)之间完全独立,这可能会导致应用程序出现错误行为。如果必须传递 SpinLock 实例,则应该通过引用而不是通过值传递。

不要将 SpinLock 实例存储在只读字段中

当锁是细粒度的并且数量巨大(例如链接的列表中每个节点一个锁)时以及锁保持时间总是非常短时,旋转可能非常有帮助