创建锁

pthread_mutex_t mtx;

但这只是定义了一个锁,在使用它前还需要进行init初始化

锁的初始化

pthread_mutex_init

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

mutex 参数表示要初始化的互斥锁;attr 参数用于自定义新建互斥锁的属性,attr 的值为 NULL 时表示以默认属性创建互斥锁。

pthread_mutex_init() 函数成功完成初始化操作时,返回数字 0;如果初始化失败,函数返回非零数。

注意,不能对一个已经初始化过的互斥锁再进行初始化操作,否则会导致程序出现无法预料的错误。

加锁与解锁

pthread_mutex_lock

pthread_mutex_lock(&mtx);

这个操作是阻塞调用的,也就是说,如果这个锁此时正在被其它线程占用, 那么 pthread_mutex_lock() 调用会进入到这个锁的排队队列中,并会进入阻塞状态, 直到拿到锁之后才会返回。

pthread_mutex_trylock

pthread_mutex_trylock(&mtx);

和 pthread_mutex_lock() 用法一样,只不过当请求的锁正在被占用的时候, 不会进入阻塞状态,而是立刻返回,并返回一个错误代码 EBUSY,意思是说, 有其它线程正在使用这个锁。

pthread_mutex_timedlock

pthread_mutex_timedlock(&mtx, &abs_timeout);

阻塞等待线程锁,但是只等待固定时间,超时后如果还没拿到锁的话, 那就返回,并返回一个错误代码 ETIMEDOUT。

pthread_mutex_unlock

pthread_mutex_unlock(&mtx);

释放互斥锁

销毁线程锁

pthread_mutex_destroy

pthread_mutex_destroy(&mtx);
  • 使用此函数销毁一个线程锁后,线程锁的状态变为”未定义”。也就是说destroy并不会释放这个锁的内存空间。
  • 一个被销毁的线程锁可以被 pthread_mutex_init() 再次初始化。
  • 对一个处于已初始化但未锁定状态的线程锁进行销毁是安全的,尽量避免对一个处于锁定状态的线程锁进行销毁操作。

条件变量

wait_for 的虚假唤醒

wait for在明面上只会在:

  1. 超时时被唤醒
  2. 被notify 唤醒。 但实际上,标准规定,它随时可能被唤醒。因为:
    • 内核实现/优化导致多唤醒了一些线程;
    • 组合多个事件源时,唤醒信号可能被“广播式”传播;
    • 某些硬件/内核调度机制本身就把“多唤醒”当正常行为; 所以我们必须要在每次唤醒都去检查谓词

pthread_cond_wait

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

其运行步骤为:

  1. 对mutex锁进行释放
  2. 阻塞,等待cond事件发生后被唤醒
  3. 被唤醒后,重新获取mutex锁

要注意的是,wait的执行过程中会把锁进行释放后再次获取锁,是为了和wait最初释放锁的动作对应。所以在wait运行结束之后,mutex仍然是被线程获取的。必须注意释放。 重点!wait走完后,是持有锁的!wait走完之后,是持有锁的!wait走完之后,是持有锁的! 重点!wait时,线程是阻塞的!wait时,线程是阻塞的!wait时,线程是阻塞的!(长久的wait是一种可能造成死锁的状态) 重点!wait时,锁是释放的!

pthread_cond_signal()

pthread_cond_signal(&cond); 

发送一条信号,用于激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个

需要注意的是,由于CPU调度的原因,在多核处理器下,signal动作可能会误唤醒多个处于wait的线程。可能导致的情况是,一个wait状态的线程被唤醒,但在其将要拿到锁的时候,另外一个处于wait状态的运行更快的线程先被唤醒并且对临界资源进行了更改。这时第一个线程到达临界资源的时候,资源已经和期望的情况不同了。 这也就是为什么wait需要和while循环配合使用的原因。为了让误唤醒的线程不要做操作,于是循环判断期望的条件,不符合的时候让误唤醒的线程再次wait。

pthread_cond_broadcast()

pthread_cond_broadcast(&cond);

激活所有等待该条件的线程。

相关笔记

  • pthread
  • 线程
  • 死锁的一次分析总结