Linux线程同步

Linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和信号量。

Linux线程同步-----互斥锁(Mutex)

互斥锁(Mutex)

与信号处理函数一样,线程在访问全局资源时也会遇到非原子操作导致的冲突(可重入问题). 比如两个线程要对同一个寄存器加1, 并行访问时可能会导致只加了一次.
不可重入操作的特点时,输出不仅依赖于输入,还依赖于状态, 比如加1 依赖于状态,这个状态是寄存器原值. 访问状态和修改状态不是原子操作的话,就会导致并发冲突

  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);

Linux线程同步条件变量(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 *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函数(block)之间被发出,从而造成无限制的等待)。

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

Linux线程同步----- 信号量

如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。
线程使用的基本信号量函数有四个:

  1. 初始化信号量
    int sem_init (sem_t *sem , int pshared, unsigned int value);
  • 参数:
    sem - 指定要初始化的信号量;
    pshared - 信号量 sem 的共享选项,linux只支持0,表示它是当前进程的局部信号量;
    value - 信号量 sem 的初始值。
  1. 信号量值加1
    给参数sem指定的信号量值加1。
    int sem_post(sem_t *sem);

  2. 信号量值减1
    给参数sem指定的信号量值减1。
    int sem_wait(sem_t *sem);
    如果sem所指的信号量的数值为0,函数将会等待直到有其它线程使它不再是0为止。

  3. 销毁信号量
    销毁指定的信号量。
    int sem_destroy(sem_t *sem);

你可能感兴趣的:(Linux线程同步)