二 Linux--多线程( 二 )


int pthread_mutex_unlock(pthread_mutex_t *mutex);功能: 对指定的互斥锁解锁参数: mutex:互斥锁地址返回值: 成功:0 失败:非0错误码

  • 销毁互斥量
int pthread_mutex_destroy(pthread_mdtex_t *mutex);功能: 销毁指定的一个互斥锁 。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源参数: mutex:互斥锁地址返回值: 成功:0 失败:非0错误码注意:
  • 使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁
  • 不要销毁一个已经加锁的互斥量
  • 已经销毁的互斥量,要确保后面不会有线程再尝试加锁
  • 加锁的粒度要够小
代码示例:写了一个抢票的小程序,用全局变量ticket代表现有票数,五个线程分别执行抢票的操作,也就是对ticket进行减减的操作,直到票数为0就停止抢票
#include <stdio.h>#include <pthread.h>#include <unistd.h>pthread_mutex_t mutex;// 创建锁变量//全局变量,所有线程共享int ticket = 10;void* get_tickets(void* arg){ long id = (long)arg; while (1){usleep(1000);// 加锁pthread_mutex_lock(&mutex);if (ticket > 0){// 有票--ticket;printf("线程%ld获得一张票,剩余%d张票\n",id,ticket);// 解锁pthread_mutex_unlock(&mutex);}else{// 无票,退出// 解锁pthread_mutex_unlock(&mutex);break;} }}int main(){ pthread_t t[5]; // 初始化锁 pthread_mutex_init(&mutex, NULL); // 创建5个线程 long i = 0; for (; i < 5; ++i) { pthread_create(t+i, NULL, get_tickets, (void*)(i+1)); } // 释放5个线程 for (i = 0; i < 5; ++i) {pthread_join(t[i], NULL); } // 销毁锁 pthread_mutex_destroy(&mutex); return 0;}运行结果如下:
二 Linux--多线程

文章插图
总结几点并回答几个问题:
锁的作用: 对临界区进行保护,所有的执行流线程都必须遵守这个规则:lock——>访问临界区——>unlock
需要注意的点:
  • 所有的线程必须看到同一把锁,锁本身就是临界资源,所以锁本身需要先保证自身安全申请锁的过程不能出现中间态,必须保证原子性
  • 任一线程持有锁之后,其它线程如果还想申请锁时申请不到的,保证互斥性
线程申请不到锁此时会做什么?
进入等待队列进行等待,从运行队列转移到等待队列,状态由R变成S,持有锁的线程unlock之后,需要唤醒等待队列中的第一个线程
struct mutex{  int lock;// 0 1     // ...     sturct wait_queue;//锁下的等待队列}互斥量的原理大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期 。下面是lock和unlock的伪代码
lock: movb $0, %a1     # 把0值放进寄存器a1里 xchgb %a1, mutex # 交换a1寄存器的内容和锁的值(无线程使用锁时,metux的值为1) if (%a1 > 0)return 0; # 得到锁 else挂起等待; goto lock;unlock: movb $1 mutex  #把1赋给锁 唤醒等待的线程; return 0;

经验总结扩展阅读