自旋锁和互斥锁的实现以及使用区别

一、自旋锁和互斥锁的实现

      基于硬件原语的一些抽象(比如:中断禁用、原子操作指令),怎么实现?可以参考清华大学操作公开课(向勇、陈渝老师讲的),以下摘抄一部分实现代码来实现抽象。

      Test And Set

bool Test_And_Set(bool* flag)
{
    bool rv = *flag;
    *flag = TRUE;
    return rv;
}

这是一条机器指令,这条机器指令完成了通常操作的读写两条机器指令的工作,完成了三件事情:

  • 从内存中读取值
  • 测试该值是否为1(然后返回真或假)
  • 内存值设置为1

       Exchange

void Exchange(bool *a, bool *b)
{
    bool tmp = *a;
    *a = *b;
    *b = tmp;
}

      虽然这两个指令看起来由几条小指令组成,但是它已经被封装成了一条机器指令,这就意味着它在执行的时候不会被打断,不允许出现中断或切换,这就是机器指令的语义保证。在此基础上完成互斥。

       这里看到如果锁状态value为1,表示临界区现在被人占用,已经上锁了,这时调用Lock::Acquire()不能得到锁,那么它会在这里死循环,不断测试value的值。这种空转全用来消耗任务的时间片,显然是可以有优化空间的。例如发现获取锁失败,则将任务置为睡眠状态,挂入到等待队列。

class Lock{
    int value = 0;
}

Lock::Acquire(){
    int key = 1;
    while(1 == key){
        exchange(lock, key);
    }
}

Lock::Reease(){
    value = 0;
}//本质上就是用一个机器指令完成以下操作1、读取当前值。2、把当前值对应的内存设置为1。

二、自旋锁和互斥锁的区别

  1. 互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销;
  2. 自旋锁:线程一直是running(加锁——>解锁),死循环检测锁的标志位,机制不复杂,主要用于SMP或内核可抢占下,因为在内核不可抢占下,cpu在执行空操作。
  3. 互斥锁的起始原始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥锁的开销造成影响,而自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长

三、自旋锁和互斥锁的对应的应用

       互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况都可以考虑

  1、 临界区有IO操作

  2 、临界区代码复杂或者循环量大

  3 、临界区竞争非常激烈

  4、 单核处理器

    至于自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下。

    自旋-互斥锁

    下面的英文介绍了混合互斥锁和混合自旋锁,但是不管是第一段说的先上非阻塞锁后上阻塞锁,还是第二段说的先自旋上锁后进行休眠,反正思路都是先自旋上锁一定时间后在上互斥锁,这种自旋-互斥锁适合各线程持锁时间间隔跨度比较大的情况。

    A hybrid mutex behaves like a spinlock at first on a multi-core system. If a thread cannot lock the mutex, it won't be put to sleep immediately, since the mutex might get unlocked pretty soon, so instead the mutex will first behave exactly like a spinlock. Only if the lock has still not been obtained after a certain amount of time (or retries or any other measuring factor), the thread is really put to sleep. If the same system runs on a system with only a single core, the mutex will not spinlock, though, as, see above, that would not be beneficial.

A hybrid spinlock behaves like a normal spinlock at first, but to avoid wasting too much CPU time, it may have a back-off strategy. It will usually not put the thread to sleep (since you don't want that to happen when using a spinlock), but it may decide to stop the thread (either immediately or after a certain amount of time) and allow another thread to run, thus increasing chances that the spinlock is unlocked (a pure thread switch is usually less expensive than one that involves putting a thread to sleep and waking it up again later on, though not by far).

原文地址:https://www.cnblogs.com/kwdeblog/p/12846724.html