计算机操作系统基础十二线程同步之自旋锁
引言本文为第十二篇,线程同步之自旋锁,在上一篇文章介绍了互斥量,通过互斥量解决线程同步的问题。本文是另一个解决线程同步的方法---自旋锁 自旋锁自旋锁的工作原理跟互斥量的工作原理其实是一模一样的,它也是在访问临界资源之前加一个锁,完成之后再将锁给释放掉。但是互斥量和自旋锁还是有一点区别的 自旋锁也是一种多线程同步的变量使用了自旋锁的线程会反复检查锁变量是否可用,如果不可用就会循环反复的检查因此自旋锁不会让出CPU,是一种忙等待状态因此自旋锁其实就是:死循环等待锁被释放 自旋锁好处 自旋锁避免了进程或线程上下文切换的开销(如果这个锁占用的时间不是很长,这个代价还是很小的)操作系统内部很多地方都是使用自旋锁,而不是互斥量自旋锁不适合在单核CPU使用(因为自旋锁在等待的时候并不会释放CPU,如果在单核CPU使用的话会引起其它的进程或线程无法执行)自旋锁和互斥量(锁)的比较 自旋锁是一种非阻塞锁,也就是说,如果某线程需要获取自旋锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取自旋锁互斥量是阻塞锁,当某线程无法获取互斥量时,该线程会被直接挂起,该线程不再消耗CPU时间,当其他线程释放互斥量后,操作系统会激活那个被挂起的线程,让其投入运行两种锁适用的场景 如果是多核处理器,如果预计线程等待锁的时间很短,短到比线程两次上下文切换时间要少的情况下,使用自旋锁是划算的如果是多核处理器,如果预计线程等待锁的时间较长,至少比两次线程上下文切换的时间要长,建议使用互斥量如果是单核处理器,一般建议不要使用自旋锁。因为,在同一时间只有一个线程是处在运行状态,那如果运行线程发现无法获取锁,只能等待解锁,但因为自身不挂起,所以那个获取到锁的线程没有办法进入运行状态,只能等到运行线程把操作系统分给它的时间片用完,才能有机会被调度。这种情况下使用自旋锁的代价很高建议:如果加锁的代码经常被调用,但竞争情况很少发生时,应该优先考虑使用自旋锁,自旋锁的开销比较小,互斥量的开销较大 自旋锁的代码示例 #include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>$include<vector>//自旋锁定义pthread_spinlock_t spin_lock;//临界资源int num=0;//生产者void *producer(void*){ int times = 100000000;//循环一百万次 while(times--){ //加自旋锁 pthread_spin_lock(&spin_lock); num += 1;//每次生产一个产品 //解锁 pthread_spin_unlock(&spin_lock); }}//消费者void *consumer(void*){ int times = 100000000; while(times--){ //加自旋锁 pthread_spin_lock(&spin_lock); num -= 1;//每次消费一个产品 //解锁 pthread_spin_unlock(&spin_lock); }}int main(){ printf("Start in main function."); //初始化自旋锁 pthread_spin_init(&spin_lock, 0); //定义两个线程 pthread_t thread1,thread2; //一个执行生成者逻辑,一个执行消费者逻辑 pthread_create(&thread1, NULL, &producer, NULL); pthread_create(&thread2, NULL, &consumer, NULL); pthread_join(&thread1, NULL); pthread_join(&thread2, NULL); //打印临界资源的值 printf("Print in main function: num = %d\n", num); return 0;}执行结果 ...