如何让主程序票务创建函数优先级高于基于mutex的单线程
实现方案
1. 替换互斥锁为写优先读写锁
原方案用全局互斥锁会把整个同步流程都锁住,导致出票操作需要等待全量同步+上报完成才能执行,完全没必要。换成pthread_rwlock_t读写锁,并且配置为写者优先模式:
- 出票操作属于ticket db的写操作,申请写锁
- 同步线程仅在读取ticket db阶段申请读锁,读取完成立刻释放锁,后续写config db、上报服务端的操作完全不需要持有锁
- 写者优先模式下,只要有出票操作在等待锁,后续同步线程的读锁申请会被阻塞,优先保障出票操作先拿到锁执行
2. 调整线程优先级
通过pthread API设置线程调度策略和优先级,进一步保障出票操作的调度优先级:
- 处理出票逻辑的线程(或主线程)设置为
SCHED_FIFO实时调度策略,优先级设为最高级 - 同步上报线程使用默认的
SCHED_OTHER普通调度策略,优先级设为最低级
这样即使系统CPU占用很高,内核也会优先调度出票逻辑的线程执行。
3. 拆分同步线程任务粒度
把同步线程的逻辑拆成锁区和非锁区,最小化锁的持有时间:
// 锁区(仅持有锁几毫秒):读取ticket db的待同步增量数据,立刻释放锁 // 非锁区(无锁,不影响出票):写config db、上报服务端db
就算同步线程已经在执行,只要它过了读ticket db的锁区,出票操作完全不会被阻塞;就算刚好卡在锁区,因为是写优先,同步线程读完释放锁之后第一个给出票操作用。
修改后的代码示例
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <sched.h> // 全局读写锁,Linux下默认配置为写者优先 pthread_rwlock_t ticket_rwlock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP; void int_handler_on_create_ticket_key_press () { // 申请写锁,优先级高于所有读锁申请 pthread_rwlock_wrlock(&ticket_rwlock); // 出票核心逻辑,写入ticket db // Store generated ticket no in ticket db pthread_rwlock_unlock(&ticket_rwlock); } void *thread_fun (void *arg) { while(1) { // 1. 仅读ticket db阶段加读锁,不做任何耗时操作 pthread_rwlock_rdlock(&ticket_rwlock); // 读取ticket db中待同步的增量数据,拷贝到本地栈变量/堆缓存 pthread_rwlock_unlock(&ticket_rwlock); // 2. 无锁阶段,完全不影响出票操作 // 把本地缓存的待同步数据写入config db // 上报数据到服务端db // 休眠5分钟 sleep(300); } return NULL; } int main() { // 配置主线程(处理出票逻辑)为实时高优先级 struct sched_param param; param.sched_priority = sched_get_priority_max(SCHED_FIFO); pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); // 创建同步线程 pthread_t sync_thread; pthread_create(&sync_thread, NULL, thread_fun, NULL); // 配置同步线程为低普通优先级 param.sched_priority = sched_get_priority_min(SCHED_OTHER); pthread_setschedparam(sync_thread, SCHED_OTHER, ¶m); // 主逻辑监听出票触发事件 while(1) { // 实际替换为对应按键/信号监听逻辑 pause(); } return 0; }
注意事项
- 如果出票逻辑是在中断上下文触发,不要在中断处理函数里直接加锁操作,把写db逻辑投递到高优先级的用户态工作队列处理即可
- 同步线程读ticket db的时候只需要读取上次同步之后的增量数据即可,不需要全表扫描,进一步缩短锁持有时间
内容的提问来源于stack exchange,提问作者M tech expert Gmoepgihal
相关产品推荐
相关产品推荐

