线程同步的常用方式与具体的区别

1、 临界资源(CCriticalSection)/关键段 
当多个线程访问一个独占性共享资源时,可以使用临界区对象。拥有临界区的线程可以访问被保护起来的资源或代码段,其他线程若想访问,则被挂起,直到拥有临界区的线程放弃临界区为止。(临界区只能同一进程中线程使用,不能跨进程使用
临界资源:同时只允许一个进程使用的资源。 
临界区:进程中用于访问临界资源的代码段,又称临界段。 
每个进程的临界区代码可以不同,临界区代码由于要访问临界资源,因此要在进入临界区之前进行检查,至于每个进程对临界资源进行怎样的操作,这和临界资源及互斥同步管理是无关的。 
使用方式: 
1.定义临界区对象 
CcriticalSection g_CriticalSection; 
2.在访问共享资源(代码或变量)之前,先获得临界区对象 
g_CriticalSection.Lock(); 
3.访问共享资源后,则放弃临界区对象 
g_CriticalSection.Unlock(); 
临界区一般使用锁的方式来实现,常见的互斥锁和读写锁:提供对临界资源的保护,当多线程试图访问临界资源时,都必须通过获取锁的方式来访问临界资源。(临界资源:是被多线程共享的资源)当读写线程获取锁的频率差别不大时,一般采用互斥锁,如果读线程访问临界资源的频率大于写线程,这个时候采用读写锁较为合适,读写锁允许多个读线程同时访问临界资源,读写线程必须互斥访问临界资源。读写锁的实现采用了互斥锁,所以在读写次数差不多的情况下采用读写锁性能没有直接采用互斥锁来的高。 
最后总结下关键段: 
1.关键段共初始化化、销毁、进入和离开关键区域四个函数。 
2.关键段可以解决线程的互斥问题,但因为具有“线程所有权”,所以无法解决同步问题。 
3.推荐关键段与旋转锁/自旋锁配合使用。关于锁,见本文后面

(注意:下面说的3种同步手段,事件,互斥量,信号量都是内核对象,都可以跨进程使用)

2、 互斥量/互斥锁(CMutex) 
互斥量多用于多进程之间的线程互斥,用来确保一个线程独占一个资源的访问。而且能正确处理资源遗弃的问题(“遗弃”问题就是——占有某种资源的进程意外终止后,其它等待该资源的进程能否感知。,而事件与信号量都无法处理遗弃问题,更多关于遗弃问题的分析,参考此处) 
互斥对象和临界区对象非常相似,只是其允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用,但是更节省资源,更有效率。 
相关函数:

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_destroy(pthread_mutex *mutex);
int pthread_mutex_unlock(pthread_mutex *
  • 1
  • 2
  • 3
  • 4

3、 事件(CEvent)/条件变量 
事件机制,则允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。或者按照条件变量的说法,提供线程之间的一种通知机制。 
每个Cevent对象可以有两种状态:有信号状态和无信号状态。 
Cevent类对象有两种类型:人工事件和自动事件。 
相关函数:

int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);     
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);  
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);  //解除所有线程的阻塞
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4、 信号量(CSemphore) 
当需要一个计数器来限制可以使用某共享资源的线程数目时,可以使用“信号量”对象。 
信号量提供对临界资源的安全分配。如果存在多份临界资源,在多个线程争抢临界资源的情况下,向线程提供安全分配临界资源的方法。如果临界资源的数量为1,将退化为锁。 
IPC方式中也有信号量,常常配合ipc共享内存来使用,作为进程之间以及同一进程不同线程间的同步手段。 
使用方式: 
CSemaphore类对象保存了对当前访问某一个指定资源的线程的计数值,该计数值是当前还可以使用该资源的线程数目。如果这个计数达到了零,则所有对这个CSemaphore类对象所控制的资源的访问尝试都被放入到一个队列中等待,直到超时或计数值不为零为止。 
线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源数加1。 
相关函数: 
信号量函数的名字都以”sem_”打头。线程使用的基本信号量函数有四个。

#include <semaphore.h>
int sem_init (sem_t *sem , int pshared, unsigned int value);
  • 1
  • 2

这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。

两个原子操作函数:

int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
  • 1
  • 2

这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。 
sem_post:给信号量的值加1; 
sem_wait:给信号量减1;对一个值为0的信号量调用sem_wait,这个函数将会等待直到有其它线程使它不再是0为止。 
int sem_destroy(sem_t *sem); 
这个函数的作用是再我们用完信号量后都它进行清理。归还自己占有的一切资源。 
5、令牌: 
一种高级的线程同步的方法。它既提供锁的安全访问临界资源的功能,又利用了条件变量使得线程争夺临界资源时是有序的。

注意: 
1、临界区和互斥量都有“线程所有权”的概念,所以它们是不能用来实现线程间的同步的,只能用来实现互斥。 
2、事件和信号量都可以实现线程和进程间的互斥和同步。但是事件和信号量都无法解决遗弃问题。 
3、临界区的效率是最高的,因为它不是内核对象。但是临界区不能跨进程使用。 
事件,互斥量,信号量都是内核对象,可以跨进程使用,但相应的效率也会低很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值