1. 线程间通信基础进程 vs 线程开销线程创建、切换开销远小于进程。通信进程间通信IPC复杂如管道、共享内存线程间通信天然方便共享进程全局变量。可靠性进程独立性高一个进程崩溃一般不影响其他进程线程间共享内存一个线程出错可能影响整个进程。核心概念临界资源多个线程/进程共享的资源如全局变量、文件。临界区访问临界资源的那部分代码。设计原则临界区应尽可能小。原子操作不可分割的操作。要么全部执行完成要么完全不执行中间不会被任何操作打断。互斥访问同一时刻只允许一个线程访问临界资源。2. 线程同步与互斥机制一互斥锁Mutex作用保证临界资源的互斥访问实现“原子操作”效果。起到防止结果被覆盖的作用。2.1 互斥锁的“生活比喻”就像公共厕所的门锁一个人进去后锁上门加锁其他人只能在门外等待阻塞用完开门解锁下一个人才能进入。2.2 使用框架定义在全局 初始化、销毁在主函数 加锁、解锁在执行函数中。lock:阻塞 锁被占用就等待。trylock:不阻塞锁被占用就返回失败线程继续向下。eg:pthread_mutex_t mutex;动态初始化pthread_mutex_init(mutex, NULL);静态初始化pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;pthread_mutex_lock(mutex);pthread_mutex_unlock(mutex);pthread_mutex_destroy(mutex);练习创建两个子线程每个线程循环 50000 次对全局共享变量cnt执行自增操作使用pthread_mutex_trylock非阻塞尝试加锁和 lock 保护临界区避免多线程并发修改cnt引发数据竞争主线程等待两个子线程全部运行完毕后销毁互斥锁、退出程序。3. 线程同步与互斥机制二信号量Semaphore作用不仅实现互斥更重要的是实现有顺序的访问即线程同步步调一致。思考3.1 信号量的“生活比喻”像餐厅的“空桌数量”信号量有桌资源0就能入座就餐P操作资源-1吃完离开V操作资源1通知等待的人。P-申请 -sem_wait()V-释放 -sem_post()二值信号量资源数量只有0和1类似互斥锁但更强调顺序。计数信号量资源数量 1可管理多个同类资源。3.2 使用框架1、信号量的定义 sem_t sem;信号量的类型 信号量的变量sem_t sem_w;//写资源sem_t sem_r;//读资源2、信号量的初始化int sem_init(sem_t *sem, int pshared, unsigned int value);功能将已经定义好的信号量赋值。参数sem 要初始化的信号量pshared 0 ;表示线程间使用信号量!0 ;表示进程间使用信号量value 信号量的初始值一般无名信号量都是二值信号量0 10 表示红灯进程暂停阻塞1 表示绿灯进程可以通过执行返回值成功 0失败 -1//写资源sem_wsem_init(sem_w,0,1); //1 表示我有一个写资源可用sem_t sem_r;sem_init(sem_r,0,0);3、信号量的PV 操作P 》申请资源》申请一个二值信号量V 》释放资源》释放一个二值信号量P操作对应函数 》sem_wait();V操作对应函数 》sem_post();int sem_wait(sem_t *sem); //p操作 ---消费资源功能判断当前sem信号量是否有资源可用。如果sem有资源(1)则申请该资源(对应的资源数量要减1)程序继续运行如果sem没有资源(0)则线程阻塞等待一旦有资源则自动申请资源并继续运行程序。P操作的逻辑--先去申请资源有则拿来用对应资源数量减1没有则阻塞直到有资源可用注意sem 申请资源后会自动执行 sem sem - 1;参数sem 要判断的信号量资源返回值成功 0失败 -1int sem_post(sem_t *sem); //V操作 ---生产资源功能函数可以将指定的sem信号量资源释放 (释放资源表示产生了一个对应的资源 对应资源的数量加1)并默认执行sem sem1;线程在该函数上不会阻塞。(V的操作逻辑--释放资源如果有线程要用则拿去使用如果没有线程用则对应资源数量加1)参数sem 要释放资源的信号量返回值成功 0失败 -14、信号量的销毁int sem_destroy(sem_t *sem);功能使用完毕将指定的信号量销毁参数sem要销毁的信号量返回值成功 0失败 -14. 经典同步问题与实战练习4.1 练习双线程顺序打印需求线程1打印 hello线程2打印 world要求输出结果始终为 hello world!。分析用两个二值信号量控制执行顺序。sem_h初始为1表示允许线程1先打印。sem_w初始为0表示线程2需等待线程1完成。代码逻辑线程1sem_wait(sem_h)- 打印 hello -sem_post(sem_w)线程2sem_wait(sem_w)- 打印 world -sem_post(sem_h)4.2 练习生产者-消费者模型火车票售票系统需求3个售票窗口线程共同卖出100张票全局变量ticket要求票不重卖、不漏卖。分析ticket是临界资源。每个窗口卖票时需执行“读取-判断-修改”操作必须用互斥锁保证原子性。核心代码cpthread_mutex_lock(mutex); if (ticket 0) { printf(窗口%d 卖出车票 %d\n, id, ticket); ticket--; } else { // 票已售罄 } pthread_mutex_unlock(mutex);4.3 练习生产者-消费者模型排队吃饭需求3张桌子顾客生产者不断到来登记服务员消费者安排顾客就餐。分析资源桌子空桌数量。用计数信号量desk初始为3。同步有桌子时服务员才能叫号安排顾客入座。流程顾客到前台登记 - 服务员sem_wait(desk)申请到桌子 - 创建顾客线程就餐 - 就餐结束执行sem_post(desk)释放桌子。5. 进阶话题死锁Deadlock定义两个或多个线程互相等待对方持有的资源导致所有线程都无法继续执行。产生死锁的必要条件互斥资源不能被共享。持有并等待线程已持有至少一个资源并正在等待其他线程持有的资源。不可抢占资源只能由持有者主动释放。循环等待存在一个线程等待资源的循环链。常见场景线程A持有锁1等待锁2线程B持有锁2等待锁1。避免方法尽量使用pthread_mutex_trylock避免永久阻塞。规定所有线程按相同顺序获取多个锁例如先获取锁A再获取锁B。减小锁的粒度减少持有锁的时间。6. 总结对比特性互斥锁 (Mutex)信号量 (Semaphore)核心目的互斥访问临界资源同步线程执行顺序资源计数只有锁定/解锁两种状态可视为0/1可以有多个资源计数0, 1, 2...操作者同一线程成对加锁/解锁P/V操作可以由不同线程执行典型应用保护共享数据如全局变量、文件控制执行顺序、管理资源池如连接池关系互斥是特殊的同步资源数为1同步是更广义的互斥控制访问时机