Linux c线程间的同步----互斥锁、条件变量、信号量

线程

一个进程中的所有线程共享为进程分配的地址空间。所以进程地址空间中的代码段和数据段都是共享的。

如果定义一个函数在各个线程中都可以调用,定义一个全部变量,在各个线程中都可以访问到。

各线程共享资源 
- 数据段和文本(代码)段 
- 对全局变量的访问 
- 文件描述符 
- 每种信号的处理方式(SIG_IGN,SIG_DFL或自定义的信号处理函数) 
- 当前的工作目录 
- 用户的id和组id

 

各线程私有资源 
1. 线程id 
2. 上下文,包括寄存器的值,程序计数器和栈指针 
3. 栈空间 
4. error变量 
5. 信号屏蔽字 
6. 调度优先级

线程操作

创建线程

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

线程终止 
终止线程的三种情况

  • 调用pthread_exit函数退出
  • 调用pthread_cancel函数取消该线程
  • 创建线程的进程退出或整个函数结束
  • 其中一个线程执行了exec类函数执行了新的进程
void pthread_exit(void *retval);

该参数用来保存线程退出状态 
3.线程等待

 #include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

调用该函数的线程将挂起等待,直到id的线程终止。当函数是返回时,处于等待的线程资源被回收。

成功返回0,失败返回错误码

 

线程的同步与互斥

一、互斥锁(mutex)

锁机制是同一时刻只允许一个线程执行一个关键部分的代码

有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下: pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; 在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下: int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) 其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。

pthread_mutex_destroy ()用于注销一个互斥锁,API定义如下: int pthread_mutex_destroy(pthread_mutex_t *mutex) 销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

1.初始化锁

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);

其中参数 mutexattr 用于指定锁的属性(见下),如果为NULL则使用缺省属性。 
互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前有四个值可供选择: 
(1)PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。 
(2)PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。 
(3)PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。 
(4)PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争

2.阻塞加锁

int pthread_mutex_lock(pthread_mutex *mutex);

3.非阻塞加锁

int pthread_mutex_trylock(pthread_mutex_t *mutex);

该函数语义与 pthread_mutex_lock() 类似,不同的是在锁已经被占据时返回 EBUSY 而不是挂起等待。

4.解锁(要求锁是lock状态,并且由加锁线程解锁)

int pthread_mutex_unlock(pthread_mutex *mutex);

5.销毁锁(此时锁必需unlock状态,否则返回EBUSY)

int pthread_mutex_destroy(pthread_mutex *mutex);

 

 

二、条件变量(cond)

条件变量是利用线程间共享全局变量进行同步的一种机制。条件变量上的基本操作有:触发条件(当条件变为true时);等待条件,挂起线程直到其他线程触发条件。

1.初始化条件变量

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

 尽管POSIX标准中为条件变量定义了属性,但在Linux中没有实现,因此cond_attr值通常为NULL,且被忽略。

2.有两个等待函数

(1)无条件等待

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

(2)计时等待 

int pthread_cond_timewait(pthread_cond_t *cond, pthread_mutex_t *mutex,const timespec *abstime);

  如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。

无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求(用 pthread_cond_wait() 或 pthread_cond_timedwait() 请求)竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。

 3.激发条件

(1)激活一个等待该条件的线程(存在多个等待线程时按入队顺序激活其中一个)

int pthread_cond_signal(pthread_cond_t *cond);

 (2)激活所有等待线程

int pthread_cond_broadcast(pthread_cond_t *cond);

 

4.销毁条件变量 

int pthread_cond_destroy(pthread_cond_t *cond);

只有在没有线程在该条件变量上等待的时候才能销毁这个条件变量,否则返回EBUSY

说明:

  1. pthread_cond_wait 自动解锁互斥量(如同执行了pthread_unlock_mutex),并等待条件变量触发。这时线程挂起,不占用CPU时间,直到条件变量被触发(变量为ture)。在调用 pthread_cond_wait之前,应用程序必须加锁互斥量。pthread_cond_wait函数返回前,自动重新对互斥量加锁(如同执行了pthread_lock_mutex)。

        2. 当调用pthread_cond_wait()互斥量的解锁和在条件变量挂起都是自动进行的。因此,在条件变量被触发前,所有的线程都要对互斥量加锁,这种机制可保证在线程加锁互斥量和进入等待条件变量的期间,条件变量不被触发。条件变量要和互斥量相联结,以避免出现条件竞争----一个线程预备等待一个条件变量,当它真正进入等待之前另外一个线程恰好触发了该条件(条件满足信号有可能在测试条件和调用调用pthread_cond_wait函数(block)之间发出),从而造成无限制的等待。

      3. 条件变量函数不是异步信号安全的,不应当在信号处理程序中进行调用。特别要注意,如果在信号处理程序中调用 pthread_cond_signal 或 pthread_cond_boardcast 函数,可能导致调用线程死锁

 

三、信号量

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值