多线程与并发控制

发布时间:2026/7/23 12:35:47
多线程与并发控制 一、我的场景1. 有一个多线程共享的全局状态g_state需要保护互斥锁、读写锁、原子操作;2. aquireOwnership() 需要防止多进程/多线程全局抢占2. invoke_callbacks需要遍历submitted列表而不被并发修改读写锁3. swapData()用于存储数据是双缓冲机制典型的生产者-消费者模型条件变量、信号量、Lock-Free队列4.线程独立数据如correlationIDTLS5. 驱动热路径与性能优化自旋锁、无锁编程。二、需要掌握的知识锁机制互斥锁pthread_mutex_t、读写锁pthread_rwlock_t、自旋锁pthread_spin_lock的区别和使用场景无锁编程原子操作atomic_add、CAS、内存序memory_order、Lock-Free队列线程局部存储TLS__thread或thread_local用于correlationID这样的线程绑定数据同步原语条件变量pthread_cond_t、信号量、栅栏Barrier死锁和竞态锁的粒度、锁的顺序、ABBA 死锁、race condition 的检测和修复三、锁机制互斥锁1. 独占访问其它线程挂起睡觉2. 上下文切换开销大但不浪费CPUpthread_mutex_lock(g_stateLock); // 检查并修改 g_state ... pthread_mutex_unlock(g_stateLock);自旋锁1. 忙等死循环轮询2. 避免上下文切换的巨大开销但消耗CPUpthread_spin_lock每次启动检查g_state.enabled标志读写锁1. 读共享只要没人在写2. 写独占等待所有读者离开pthread_rwlock_t1. 写性能分析工具初始化的时候很少注册/注销2. 读每次launch kernel的时候都要遍历列表来trigger回调读写锁让多个CPU线程并行读取四、无锁编程与原子操作无锁编程是通过CPU原子指令直接操作数据状态切换更快且天然免疫死锁。1. CASCompare And Swap核心指令CAS(addr, old, new)if *addr old) { addr new return true } // 示例 int expected 0; // 原子比较 g_state.ownerPID 和 0相等则换成 currentPID if (atomic_compare_exchange_strong(g_state.ownerPID, expected, currentPID)) { g_state.mode mode; // 拿到锁安全初始化 } else if (g_state.ownerPID currentPID) { // 自己已持有锁正常继续 } else { return CUPROF_ERROR_HARDWARE_BUSY; }2. Lock-Free队列可以用于实现动态、零阻塞的数据传输。五、线程局部传输TLS核心per thread拥有独立变量副本不干扰。用__thread声明六、同步原语条件变量(pthread_cond_t)带条件的检查。必须配合互斥锁。信号量(sem_t)带计数器的检查。内部有个计数器。wait减1请求通过post加1释放资源。栅栏 (pthread_barrier_t)等待全部到齐。所有参与线程都必须到达wait点才一起放行。七、死锁与竞态条件死锁 (Deadlock)循环等待。最经典的是ABBA死锁。线程A拿锁1等锁2线程B拿锁2等锁1互相卡死。场景g_stateLock和一个后续要加的g_subscriberLock之间极易发生。解法是保证所有代码路径的加锁顺序一致比如永远先锁状态再锁订阅者。竞态条件 (Race Condition)结果取决于多线程执行的相对速度。TOCTOU (Time-of-check to time-of-use)在检查和操作之间数据变了。比如无锁版本里的g_perfState.ownerPID 0检查和后续的 CAS 修改不在一个原子操作里完成中间就可能被抢断这就是必须用atomic_compare_exchange_strong的原因。八、选择指南读写高频场景(如订阅者列表)首选读写锁。全局状态低频操作(如获取所有权)用互斥锁足够了简单安全。热路径标志位/计数器(如g_state.enabled)一定要用原子操作避免性能灾难。自旋锁是次选。线程绑定数据的传递(correlationID)TLS是最佳实践不要用全局变量加锁。跨线程的数据通知(Profiling 数据就绪)条件变量和信号量是标准答案不要用sleep循环去轮询。