线程的分离、同步

有关线程分离

->在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached)。
->一个可结合的线程能够被其他线程回收其资源和杀死。在被其他线程回收之前,它的存储器资源(栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或者杀死的,它的存储器资源在它终止时由系统自动释放
->默认情况下,线程被创建程可结合的。为了避免存储器泄露,每个可结合线程,要么不饿显示的回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。
->如果一个可结合的线程结束运行但没有被join,则它的状态类似于僵尸进程。
->由于调用pthread_join后,如果该线程没有运行结束,调用者会被阻塞。

  • eg:在web服务器中档主线程为每个新来的连接请求创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join二阻塞(因为还要处理之后盗来的连接请求),可以再子线程代码中加入:pthread_detach(pthread_self());或者 父线程调用pthread_detach(pthread_id)(非阻塞,立即返回)
  • 这将该子线程的状态设置为分离的(detached),如此一来,该线程运行后会自动释放所有资源
    ->模拟在设置分离后,依旧等待的状况:
    这里写图片描述
    这里写图片描述
    运行结果如下:
    这里写图片描述

线程同步

A mutex(互斥量)
->多个线程同时访问共享数据是可能会冲突。
->例如:两个线程都要把某个全局变量+1,这个操作在某个平台上需要3步:
1.从内存读变量值到寄存器
2.寄存器质+1
3.将寄存器值写回内存
->我们来通过一个简单的程序来观察这一现象:
这里写图片描述

我们创建两个线程各自把count+5000,正常情况下最终结果是10000,但事实每次运行的结果都不一样。

这里写图片描述

对于多线程的程序,访问冲突是很普遍的问题,解决这个问题要引入互斥锁(mutex)。获得锁的程序可以执行“读–修改–写”然后释放锁给其他线程,没有获得锁的线程只能等待而不能访问共享数据。

Mutex用pthread_mutex_t类型的变量表示,可以这样初始化和销毁:
这里写图片描述

返回值:成功返回0,失败返回错误码。

pthread_mutex_init函数对Mutex进行初始化,attr设定Mutex的属性,如果为空则表示缺省属性。

用pthread_mutex_init初始化的Mutex可以用pthread_mutex_destory函数销毁。如果Mutex变量是静态分配的(全局变量或者static变量),也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化,相当于用pthread_mutex_init初始化并且attr参数 为NULL。

Mutex的加锁解锁操作用以下函数:
这里写图片描述

返回值:成功返回0,失败返回错误号。
一个线程可以调用pthread_mutex_lock获得Mutex,如果这是另一个线程已经获得了该Mutex,则当前线程需要挂起等待,知道另一个线程调用pthread_mutex_destory释放Mutex,当前线程才被唤醒,才能获得该Mutex继续执行。

如果一个线程既想获得锁又不想挂起等待,可以调用pthread_mutex_trylock,如果Mutex已经被另一个线程获得,这个函数会失败返回EBUSY,而不会让线程挂起等待。

lock 和 unlock :
这里写图片描述

unlock操作中的唤醒等待线程可以有不同的实现:可以只唤醒一个等待线程,也可以唤醒所有的等待线程,让他们去竞争获得这个Mutex,没有竞争到的线程继续挂起等待 。

为了实现互斥锁:大多数操作系统都提供了swap或者exchange指令,作用是:把寄存器和内存单元的数据交换,保证了原子性。即使是多处理器平台,访问内存的总线周期也有先后顺序,一个处理器上的交换指令执行时,另一个处理器的交换指令只能等待总线周期。

死锁:一般情况下,如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待博得线程释放锁,然而锁是由自己占用着,该线程又被挂起没有机会释放锁,这样就永远处于挂起状态了。

写程序时应该尽量避免同时获得多个锁,如果一定要,则有:如果所有线程在需要多个锁时都按相同的先后顺序获得锁(按Mutex变量的地址顺序),则不会出现死锁。

B Condition Variable(条件变量)

线程1需要等某个条件成立才能继续执行,现在这个条件不成立,线程1等待阻塞,线程2在执行过程中是这个条件成立了,就唤醒线程1 继续执行。

pthread中通过条件变量来阻塞等待一个条件,或者唤醒等待这个条件的线程。
Condition Variable用pthread_cond_t类型的变量表示:

这里写图片描述

返回值:成功返回0,失败返回错误码。

条件变量有以下操作:
这里写图片描述
这里写图片描述

返回值:成功返回0,失败返回错误码。

一个Condition Variable 总是和 Mutex 搭配使用。
一个线程可以调用pthread_cond_wait在一个Condition Variable上阻塞等待,这个函数做以下操作:
1.释放Mutex
2.阻塞等待
3.当被唤醒时,重新获得Mutex并返回。

C semaphore(信号量)

Mutex变量非0即1。
semaphore与Mutex相似,不同的是数量可以大于1。
即,如果信号量描述的资源数目是1 ,则信号量就和互斥锁相同。
POSIX版 semaphore,不仅可以用于同一进程的线程间同步,也可以用于不同进程间的同步。

这里写图片描述

semaphore变量类型是 sem_t,sem_init()初始化一个semaphore变量,value表示可用资源的数量,pshared参数为0便是信号量用于同一进程的线程间同步。用完semaphore变量后应该调用sem_destory()释放与semaphore相关的资源。

调用sem_wait()可以获得资源(P操作),使semaphore得值-1,如果调用sen_wait()时semaphore的值已经为0,则挂起等待。如果不希望挂起等待,就调用sem_trywait()。调用sem_post()可是释放资源 (V操作),使semaphore得值+1,并且唤醒挂起等待线程。

D 读写锁

读写锁实际上就是一种自旋锁,他把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者只对共享资源进行写操作。
这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性,一个读写锁同时只能有一个写者或多个读者(与CPU数目有关),但不能同时既有读者又有写者

这里写图片描述

读写锁操作:

这里写图片描述
这里写图片描述
这里写图片描述

返回值:成功返回0;失败返回错误码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值