互斥锁是一种阻塞锁。当线程无法获取互斥锁时,该线程将被直接挂起。该线程将不再消耗CPU时间。当其他线程释放互斥体时,操作系统将激活挂起的线程。线程并让它运行。
两种类型的锁适用于不同的场景:
如果是多核处理器,如果预计线程等待锁的时间很短,比线程两次切换上下文的时间短,那么使用自旋锁是比较划算的。
如果是多核处理器,并且如果预计线程等待锁的时间较长,至少长于两次线程上下文切换的时间,建议使用互斥锁。
如果是单核处理器,一般建议不要使用自旋锁。因为同一时刻只有一个线程处于运行状态,如果运行的线程发现自己无法获取锁,就只能等待解锁。但由于不挂起,获得锁的线程没有办法进入运行状态,只能等待,直到运行的线程用完操作系统分配给它的时间片,才有机会被调度。在这种情况下使用自旋锁的成本很高。
如果加锁代码经常被调用,但竞争很少发生,则应优先使用自旋锁。自旋锁的成本相对较小,互斥锁的成本较大。
POSIX线程(简称Pthreads)是多核平台上并行编程常用的API。线程同步是并行编程中非常重要的通信方式。最典型的应用是使用Pthreads提供的锁机制(lock)来保护多个线程之间共享的临界区(否则常用的同步机制就是屏障)。
Pthreads 提供了多种锁定机制:
(1)互斥体(mutex):pthread_mutex_***
(2)自旋锁:pthread_spin_***
(3)条件变量:pthread_con_***
(4)读/写锁:pthread_rwlock_***
Pthreads提供的与Mutex锁操作相关的API主要包括:
pthread_mutex_lock (pthread_mutex_t *mutex);
pthread_mutex_trylock (pthread_mutex_t *mutex);
pthread_mutex_unlock (pthread_mutex_t *mutex);
Pthreads提供的与Spin Lock操作相关的API主要包括:
pthread_spin_lock (pthread_spinlock_t *lock);
pthread_spin_trylock (pthread_spinlock_t *lock);
pthread_spin_unlock (pthread_spinlock_t *lock);
从实现原理上来说,Mutex是一种睡眠等待类型的锁。例如,双核机器上有两个线程(线程A和线程B),分别运行在Core0和Core1上。假设线程A想要通过pthread_mutex_lock操作获得临界区锁,而这个锁当前被线程B持有,那么线程A就会被阻塞(blocking),此时Core0会进行一次上下文切换(Context)。 Switch)将线程A放入等待队列中。此时Core0可以运行其他任务(比如另一个线程C),无需忙等待。自旋锁则不然。它是一种忙等待类型的锁。如果线程A使用pthread_spin_lock操作来请求锁,那么线程A将一直忙于等待Core0并不断发出锁请求,直到获得锁。直到。
因此,自旋锁一般用在多核服务器上。
自旋锁
自旋锁有点类似于互斥锁,只不过自旋锁不会导致调用者休眠。如果自旋锁已经被另一个执行单元持有,调用者将继续循环以查看自旋锁的持有者是否已被持有。锁被释放,因此出现术语“自旋”。它的作用是解决某种资源的互斥使用。由于自旋锁不会导致调用者休眠,因此自旋锁比互斥锁效率更高。虽然它比互斥锁更高效,但它也有一些缺点:
1、自旋锁始终占用CPU。它一直运行——自旋而不获取锁,因此它占用了CPU。如果短时间内无法获取锁,这无疑会降低CPU的效率。
2.使用自旋锁时可能会出现死锁,递归调用时可能会出现死锁,调用其他一些函数也可能出现死锁,如copy_to_user()、copy_from_user()、kmalloc()等。
因此,我们必须谨慎使用自旋锁。仅当内核是可抢占的或SMP 时才真正需要自旋锁。在单CPU和不可抢占的内核下,自旋锁操作是无操作的。自旋锁适用于锁用户持有锁时间较短的情况。
自旋锁的用法如下:
首先定义:spinlock_t x;
然后初始化:spin_lock_init(spinlock_t *x); //自旋锁在实际使用之前必须初始化。
在2.6.11 内核中将定义和初始化合并到一个宏中:DEFINE_SPINLOCK(x)
获取自旋锁:spin_lock(x); //只有获得锁时才返回,否则会一直“自旋”
spin_trylock(x); //如果立即获得锁则返回true,否则立即返回false
释放锁:spin_unlock(x);
将以上内容与以下代码片段结合起来:
spinlock_t 锁; //定义一个自旋锁
spin_lock_init(锁);
自旋锁(锁);
. //关键部分
spin_unlock(锁定); //释放锁
还有一些其他用途:
spin_is_locked(x)
//该宏用于判断自旋锁x是否已被某个执行单元持有(即被锁定)。如果是,则返回true,否则返回false。
自旋解锁等待(x)
//该宏用于等待自旋锁x变得不被任何执行单元持有。如果没有执行单元持有自旋锁,则宏立即返回,否则
//将在那里循环,直到自旋锁被持有者释放。
spin_lock_irqsave(锁,标志)
//该宏获取自旋锁,同时将标志寄存器的值保存到变量flags中并使本地中断无效//.相当于:spin_lock()+local_irq_save()
spin_unlock_irqrestore(锁定,标志)
//该宏释放自旋锁的同时,还将标志寄存器的值恢复为//变量flags中保存的值。它与spin_lock_irqsave 配对。
//相当于:spin_unlock()+local_irq_restore()
spin_lock_irq(锁)
//该宏与spin_lock_irqsave类似,只不过该宏不保存标志寄存器的值。相当于:spin_lock()+local_irq_disable()
spin_unlock_irq(锁定)
//该宏释放自旋锁并启用本地中断。它与spin_lock_irq 结合应用。相当于: spin_unlock()+local_irq+enable()
spin_lock_bh(锁)
//该宏在获取自旋锁的同时使本地软中断无效。相当于: //spin_lock()+local_bh_disable()
spin_unlock_bh(锁定)
//该宏释放自旋锁,同时也使能本地软中断。 //它与spin_lock_bh 配对。相当于:spin_unlock()+local_bh_enable()
spin_trylock_irqsave(锁,标志)
//如果该宏获得了自旋锁,还会将标志寄存器的值保存到变量flags中,并使本地中断无效。如果没有获得锁,则不会执行任何操作。所以如果能立即获得锁,就相当于spin_lock_irqsave,如果不能获得锁,就相当于spin_trylock。如果该宏//获得了自旋锁,则需要使用spin_unlock_irqrestore 来释放它。
spin_trylock_irq(锁)
//该宏与spin_trylock_irqsave类似,只不过该宏不保存标志寄存器。如果该宏获得了自旋锁,则需要使用spin_unlock_irq 来释放它。
spin_trylock_bh(锁)
//如果该宏获得了自旋锁,也会使本地软中断失效。如果它无法获得锁,则//什么也不做。因此,如果获得锁,则相当于spin_lock_bh,如果没有获得锁,则相当于spin_trylock。如果该宏获得了自旋锁,则需要使用spin_unlock_bh 来释放它。
【深入解析:自旋锁与互斥量的差异对比】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
学习C++多线程,这两种锁真是太重要了!
有16位网友表示赞同!
我感觉自旋锁在轻量级的情况下性能更好吧?
有9位网友表示赞同!
互斥量是不是更常见一点?
有19位网友表示赞同!
什么时候应该选择哪种锁啊?没找到一个清晰的标准。
有16位网友表示赞同!
刚开始接触多线程,这两个概念还是很难理解…
有15位网友表示赞同!
这篇文章讲得浅显易懂,很好入门!
有11位网友表示赞同!
自旋锁看起来简单很多,但应用场景会不会比较有限呢?
有7位网友表示赞同!
Mutex是不是更容易实现啊?
有20位网友表示赞同!
想了解更多关于不同场景下使用哪种锁的案例分析!
有15位网友表示赞同!
这个标题太棒了!点进来刚好可以了解一下区别的原理。
有7位网友表示赞同!
感觉互斥量比自旋锁更安全吧?
有5位网友表示赞同!
哪个锁更好取决于具体情况啊,没有绝对的好坏。
有11位网友表示赞同!
学习完这篇文章后,我终于对自旋锁和互斥量有了一个初步的了解!
有17位网友表示赞同!
文章内容非常全面,涵盖了比较关键的知识点。
有9位网友表示赞同!
自旋锁真的比传统方式效率高吗?
有19位网友表示赞同!
这篇文章让我觉得多线程编程的可玩性很高!
有8位网友表示赞同!
学习编程真是充满挑战,但也很有趣啊!
有7位网友表示赞同!
以后遇到类似的排序问题,应该优先考虑自旋锁吗?
有5位网友表示赞同!
多线程是未来软件发展的趋势!
有6位网友表示赞同!