标签:critical release mutual 生产者 消费者
其实在操作系统中,都存在着多进程与多线程来优化处理整个操作系统的逻辑,不仅仅在操作系统,我们所编写的程序中都一样存在着各种地方使用多进程多线程,虽然多进程多线程看着很好很强大,但是总存在着非原子性情况下的执行顺序确定操作。
所以就出现了信号量,条件变量,互斥锁等各种确保多线程/进程的执行顺序。
信号量的可以查看:
互斥体(互斥锁)
互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex))。互斥体禁止多个线程同时进入受保护的代码“临界区”(critical section)。因此,在任意时刻,只有一个线程被允许进入这样的代码保护区。
任何线程在进入临界区之前,必须获取(acquire)与此区域相关联的互斥体的所有权。如果已有另一线程拥有了临界区的互斥体,其他线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放(release)该互斥体。
什么时候需要使用互斥体呢?互斥体用于保护共享的易变代码,也就是,全局或静态数据。这样的数据必须通过互斥体进行保护,以防止它们在多个线程同时访问时损坏。
以上转自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/21/2602015.html
关于线程的相关使用:Linux之线程:同步与互斥
三、自旋锁
自旋锁它是为为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。
自旋锁一般原理
跟互斥锁一样,一个执行单元要想访问被自旋锁保护的共享资源,必须先得到锁,在访问完共享资源后,必须释放锁。如果在获取自旋锁时,没有任何执行单元保持该锁,那么将立即得到锁;如果在获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的保持者释放了锁。由此我们可以看出,自旋锁是一种比较低级的保护数据结构或代码片段的原始方式,这种锁可能存在两个问题:死锁和过多占用cpu资源。
自旋锁适用情况
自旋锁比较适用于锁使用者保持锁时间比较短的情况。正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文使用,而自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用。如果被保护的共享资源只在进程上下文访问,使用信号量保护该共享资源非常合适,如果对共享资源的访问时间非常短,自旋锁也可以。但是如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。自旋锁保持期间是抢占失效的,而信号量和读写信号量保持期间是可以被抢占的。自旋锁只有在内核可抢占或SMP(多处理器)的情况下才真正需要,在单CPU且不可抢占的内核下,自旋锁的所有操作都是空操作。另外格外注意一点:自旋锁不能递归使用。
关于自旋锁的定义以及相应的API
自旋锁定义: linux/Spinlock.h
typedef struct spinlock { union { //联合 struct raw_spinlock rlock; #ifdef CONFIG_DEBUG_LOCK_ALLOC # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) struct{ u8 __padding[LOCK_PADSIZE]; struct lockdep_map dep_map; };#endif }; } spinlock_t;
定义和初始化
spinlock_t my_lock = SPIN_LOCK_UNLOCKED; spin_lock_init(spinlock_t *);
自旋锁操作:
//加锁一个自旋锁函数 void spin_lock(spinlock_t *lock); //获取指定的自旋锁void spin_lock_irq(spinlock_t *lock); //禁止本地中断获取指定的锁 void spin_lock_irqsave(spinlock_t *lock, unsigned long flags); //保存本地中断的状态,禁止本地中断,并获取指定的锁 void spin_lock_bh(spinlock_t *lock) //安全地避免死锁, 而仍然允许硬件中断被服务 //释放一个自旋锁函数 void spin_unlock(spinlock_t *lock); //释放指定的锁 void spin_unlock_irq(spinlock_t *lock); //释放指定的锁,并激活本地中断 void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags); //释放指定的锁,并让本地中断恢复到以前的状态 void spin_unlock_bh(spinlock_t *lock); //对应于spin_lock_bh //非阻塞锁 int spin_trylock(spinlock_t *lock); //试图获得某个特定的自旋锁,如果该锁已经被争用,该方法会立刻返回一个非0值, //而不会自旋等待锁被释放,如果成果获得了这个锁,那么就返回0. int spin_trylock_bh(spinlock_t *lock); //这些函数成功时返回非零( 获得了锁 ), 否则 0. 没有"try"版本来禁止中断. //其他 int spin_is_locked(spinlock_t *lock); //和try_lock()差不多
四、信号量、互斥体和自旋锁的区别
信号量/互斥体和自旋锁的区别
信号量/互斥体允许进程睡眠属于睡眠锁,自旋锁则不允许调用者睡眠,而是让其循环等待,所以有以下区别应用
1)、信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因而自旋锁适合于保持时间非常短的情况
2)、自旋锁可以用于中断,不能用于进程上下文(会引起死锁)。而信号量不允许使用在中断中,而可以用于进程上下文
3)、自旋锁保持期间是抢占失效的,自旋锁被持有时,内核不能被抢占,而信号量和读写信号量保持期间是可以被抢占的
另外需要注意的是
1)、信号量锁保护的临界区可包含可能引起阻塞的代码,而自旋锁则绝对要避免用来保护包含这样代码的临界区,因为阻塞意味着要进行进程的切换,如果进程被切换出去后,另一进程企图获取本自旋锁,死锁就会发生。
2)、在你占用信号量的同时不能占用自旋锁,因为在你等待信号量时可能会睡眠,而在持有自旋锁时是不允许睡眠的。
信号量和互斥体之间的区别
概念上的区别:
信号量:是进程间(线程间)同步用的,一个进程(线程)完成了某一个动作就通过信号量告诉别的进程(线程),别的进程(线程)再进行某些动作。有二值和多值信号量之分。
互斥锁:是线程间互斥用的,一个线程占用了某一个共享资源,那么别的线程就无法访问,直到这个线程离开,其他的线程才开始可以使用这个共享资源。可以把互斥锁看成二值信号量。
上锁时:
信号量: 只要信号量的value大于0,其他线程就可以sem_wait成功,成功后信号量的value减一。若value值不大于0,则sem_wait阻塞,直到sem_post释放后value值加一。一句话,信号量的value>=0。
互斥锁: 只要被锁住,其他任何线程都不可以访问被保护的资源。如果没有锁,获得资源成功,否则进行阻塞等待资源可用。一句话,线程互斥锁的vlaue可以为负数。
使用场所:
信号量主要适用于进程间通信,当然,也可用于线程间通信。而互斥锁只能用于线程间通信。
以上转自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/21/2602015.html
读写锁:
在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相比较改写,它们读的机会反而高的多。通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效率。那么有没有一种方法,可以专门处理这种多读少写的情况呢?有,那就是读写锁。
读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。
读写锁相关的操作函数:
读写锁操纵:
函数整体线程之前的讲解比较像,所以直接上代码吧:
//阻塞读写锁模式: #include<stdio.h> #include<pthread.h> int g_val = 0; pthread_rwlock_t rw_lock; void* reader(void* arg) { //阻塞读写锁。 while(1) { pthread_rwlock_rdlock(&rw_lock); printf("g_val: %d\n",g_val); pthread_rwlock_unlock(&rw_lock); } } void* writer(void* arg) { while(1) { sleep(1); pthread_rwlock_wrlock(&rw_lock); g_val++; pthread_rwlock_unlock(&rw_lock); } } int main() { pthread_t tid1,tid2; pthread_rwlock_init(&rw_lock,NULL); pthread_create(&tid1,NULL,reader,NULL); pthread_create(&tid2,NULL,writer,NULL); pthread_rwlock_destroy(&rw_lock); pthread_join(tid1,NULL); pthread_join(tid2,NULL); return 0; }
运行结果:
运行结果就是写者每隔一秒进行一次写,读者一直在读。结果截图太长了,所以就不截图了。
非阻塞模式:
#include<stdio.h> #include<pthread.h> int g_val = 0; pthread_rwlock_t rw_lock; void* reader(void* arg) { pthread_detach(pthread_self()); //非阻塞读写锁。 while(1) { if(pthread_rwlock_tryrdlock(&rw_lock)!= 0){ printf("writer is wirting,reader waiting...\n"); } else{ printf("reader is:%u,g_val: %d\n",pthread_self(),g_val); pthread_rwlock_unlock(&rw_lock); } sleep(2); } } void* writer(void* arg) { pthread_detach(pthread_self()); while(1) { if(pthread_rwlock_trywrlock(&rw_lock) != 0){ printf("reader is reading,writer waiting\n"); sleep(1); } else{ g_val++; printf("writer is :%u,writer val is :%d\n",pthread_self(),g_val); pthread_rwlock_unlock(&rw_lock); } sleep(1); } } int main() { pthread_rwlock_init(&rw_lock,NULL); pthread_t id; int i ; for(i = 0;i<2;++i) { pthread_create(&id, NULL,writer,NULL); } for(i = 0;i < 3;++i) { pthread_create(&id,NULL,reader,NULL); } sleep(100); return 0; }
运行结果:
本文出自 “剩蛋君” 博客,请务必保留此出处http://memory73.blog.51cto.com/10530560/1770804
标签:critical release mutual 生产者 消费者
原文地址:http://memory73.blog.51cto.com/10530560/1770804