pthread中的lock、unlock、wait、signal

1. 锁与条件变量之初始化

  • 静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 
  • 动态初始化
int pthread_cond_init(pthread_cond_t *cv,const pthread_condattr_t *cattr);//成功返回0
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);//成功返回0

2. 使用方法

  • 等待(消费者/consumer)线程
    pthread_mutex_lock //wait之前先加锁
    pthread_cond_wait //内部会解锁,然后等待条件变量被其它线程激活,然后再重新上锁
    pthread_mutex_unlock //执行完后解锁
  • 激活(生产者/producer)线程
    pthread_mutex_lock //防止共享数据在访问
    pthread_mutex_unlock //这里后面讲为什么要先解锁然后再发送信号
    pthread_cond_singal //
  • pthread_mutex_trylock() //非阻塞的锁定互斥锁

3. 一些疑问

  • 针对消费者线程内部加锁/解锁机制的疑问?
    一般在wait之前,都会先加上锁,如果没有信号来通知消费者线程,那么线程便会调用pthread_cond_wait阻塞自己,但是它持有的锁怎么办呢,如果他不归还操作系统,那么其他线程将会无法访问公有资源。这就要追究一下pthread_cond_wait的内部实现机制:当pthread_cond_wait被调用线程阻塞的时候,pthread_cond_wait会自动释放互斥锁。释放互斥锁的时机是什么呢:是线程从调用pthread_cond_wait到操作系统把他放在线程等待队列之后,这样做有一个很重要的原因,就是mutex的第二个作用,保护条件。想一想,线程是并发执行的,如果在没有把被阻塞的线程A放在等待队列之前,就释放了互斥锁,假如此时线程B可以获得互斥锁去访问公有资源,触发了A等待的条件,但是A还没有被放在等待队列上,导致A忽略了等待条件被满足的信号。倘若在线程A调用pthread_cond_wait开始,到把A放在等待队列的过程中,都持有互斥锁,其他线程无法得到互斥锁,就不能改变公有资源。这就保证了线程A被放在等待队列上之后才会有公有资源被改变的信号传递给等待队列
  • 针对于生产者线程为什么要先unlock然后再发送singal的疑问?
    当一个等待线程被唤醒的时候,它必须首先加锁互斥量。如果消费者线程被唤醒而此时生产者线程仍然锁住互斥量,则消费者线程会立刻阻塞在互斥量上,等待生产者线程解锁该互斥量,引起线程的上下文切换。当生产者线程解锁后,消费者线程继续获得锁,再一次的引起上下文切换。这样导致消费者线程不能顺利加锁,延长了加锁时间,加重了系统不必要的负担。
  • 使用while还是if来判断线程执行(等待)条件成立的判断?
while(q.empty()) or if(q.empty())
	pthread_cond_wait(&cond, &mutex);

答案:最好是使用while循环,因为可能存在“虚假”唤醒的情况。假如现在有A、B两个线程同时等待生产者线程来唤醒,当B线程准备获得队列的锁,去获取队列中的元素时,此时A线程刚好执行完之前的元素操作,返回再去请求队列中的元素,A线程便获得队列的锁,检查到队列非空,就获取到了生产者线程刚刚入队的元素,然后释放队列锁。 等到B线程获得队列锁,判断发现队列仍为空,A线程“偷走了”这个元素,所以对于B线程而言,这次唤醒就是“虚假”的,它需要再次等待队列非空。
但如果对于只有一个等待线程的情况,使用if也是可以的。

  • 如果有多个等待线程,那么执行pthread_cond_signal结果会如何呢
    使用pthread_cond_signal一般不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那 么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一 个pthread_cond_signal调用最多发信一次。
    但是pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程.
  • pthread_cond_wait能否使用已经错过的信号
    假如发送信号的频率比等待的要快,那么多发送的信号都将视为无效。 也就是说,如果signal的时候没有线程在wait,那么本次signal就没有效果,后续的线程进入 wait之后,无法被之前的signal唤醒。也可以理解为等待必须先与信号发送前,否则无效。

猜你喜欢

转载自blog.csdn.net/u013187057/article/details/86549736