1. Linux线程控制基础在Linux系统中线程是程序执行的最小单位而进程则是资源分配的最小单位。理解线程控制是掌握多线程编程的基础我们先来看一个最基本的线程创建示例#include pthread.h #include stdio.h void* thread_func(void* arg) { printf(New thread created\n); return NULL; } int main() { pthread_t tid; int ret pthread_create(tid, NULL, thread_func, NULL); if(ret ! 0) { perror(pthread_create failed); return 1; } pthread_join(tid, NULL); return 0; }这个简单例子展示了线程创建的基本流程但实际开发中我们需要考虑更多细节。pthread_create函数的四个参数分别是线程ID指针线程属性通常设为NULL使用默认值线程函数传递给线程函数的参数注意创建线程后必须调用pthread_join或pthread_detach否则会导致资源泄漏。join会阻塞当前线程直到目标线程结束而detach则让线程在结束后自动释放资源。1.1 线程属性控制线程属性可以通过pthread_attr_t结构体进行精细控制以下是常见属性的设置示例pthread_attr_t attr; pthread_attr_init(attr); // 设置线程为分离状态 pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED); // 设置栈大小单位字节 size_t stack_size 1024*1024; // 1MB pthread_attr_setstacksize(attr, stack_size); pthread_t tid; pthread_create(tid, attr, thread_func, NULL); pthread_attr_destroy(attr);实际项目中合理设置线程属性可以避免许多潜在问题。比如在嵌入式系统中栈大小需要特别关注过小会导致栈溢出过大则浪费内存。2. 线程终止的三种方式线程终止不是简单地让它停止运行而是需要确保资源被正确释放。Linux线程有三种终止方式2.1 从线程函数返回这是最推荐的终止方式线程函数执行完毕自然退出void* thread_func(void* arg) { // 线程工作代码 return (void*)42; // 返回退出码 }返回的值可以通过pthread_join获取void* retval; pthread_join(tid, retval); printf(Thread exited with code %d\n, (int)retval);2.2 调用pthread_exit线程可以在任何地方调用pthread_exit主动退出void* thread_func(void* arg) { if(error_condition) { pthread_exit((void*)1); // 提前退出 } // 正常执行 pthread_exit((void*)0); }警告在主线程中调用pthread_exit会导致程序继续运行直到所有线程结束这可能不是你想要的行为。2.3 被其他线程取消线程可以被同一进程中的其他线程取消pthread_cancel(tid); // 请求取消线程被取消的线程需要在适当的位置设置取消点void* thread_func(void* arg) { // 设置取消类型 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); while(1) { // 这是一个取消点 pthread_testcancel(); // 工作代码 } return NULL; }取消线程是危险操作可能导致资源泄漏应该尽量避免使用。3. 线程异常问题处理多线程环境下的异常处理比单线程复杂得多下面介绍几种常见问题及解决方案。3.1 竞态条件当多个线程同时访问共享资源时可能产生竞态条件。看这个典型例子int counter 0; void* increment(void* arg) { for(int i 0; i 100000; i) { counter; // 这不是原子操作 } return NULL; }运行两个这样的线程后counter的值通常不会如预期的200000。解决方法是用互斥锁pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; int counter 0; void* increment(void* arg) { for(int i 0; i 100000; i) { pthread_mutex_lock(mutex); counter; pthread_mutex_unlock(mutex); } return NULL; }3.2 死锁问题死锁通常发生在多个锁的使用场景中。例如// 线程1 pthread_mutex_lock(mutexA); pthread_mutex_lock(mutexB); // 操作共享资源 pthread_mutex_unlock(mutexB); pthread_mutex_unlock(mutexA); // 线程2 pthread_mutex_lock(mutexB); pthread_mutex_lock(mutexA); // 操作共享资源 pthread_mutex_unlock(mutexA); pthread_mutex_unlock(mutexB);避免死锁的几个原则总是以相同的顺序获取锁使用pthread_mutex_trylock而非阻塞锁设置锁超时时间3.3 条件变量的正确使用条件变量常用于线程间同步但使用不当会导致问题pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond PTHREAD_COND_INITIALIZER; bool ready false; // 等待线程 void* waiter(void* arg) { pthread_mutex_lock(mutex); while(!ready) { pthread_cond_wait(cond, mutex); } // 处理条件满足的情况 pthread_mutex_unlock(mutex); return NULL; } // 通知线程 void* notifier(void* arg) { pthread_mutex_lock(mutex); ready true; pthread_cond_signal(cond); pthread_mutex_unlock(mutex); return NULL; }常见错误包括在调用pthread_cond_wait前未加锁使用if而非while检查条件修改条件后忘记通知4. 线程局部存储线程局部存储(TLS)允许每个线程拥有变量的独立副本非常适合存储线程特定数据__thread int tls_var 0; // GCC扩展语法 void* thread_func(void* arg) { tls_var (int)(long)arg; printf(Thread %ld has tls_var %d\n, (long)pthread_self(), tls_var); return NULL; }POSIX标准也提供了pthread_key_create等函数实现TLSpthread_key_t key; void destructor(void* value) { free(value); } void init_key() { pthread_key_create(key, destructor); } void* thread_func(void* arg) { int* data malloc(sizeof(int)); *data (int)(long)arg; pthread_setspecific(key, data); // 获取线程特定数据 int* value pthread_getspecific(key); printf(Value %d\n, *value); return NULL; }5. 线程安全函数设计编写线程安全函数需要遵循以下原则避免使用全局变量和静态变量如果必须使用共享数据用互斥锁保护使用线程安全版本的库函数如strtok_r替代strtok避免在函数内分配需要调用者释放的内存一个线程安全的计数器实现示例typedef struct { int value; pthread_mutex_t mutex; } ThreadSafeCounter; void counter_init(ThreadSafeCounter* counter) { counter-value 0; pthread_mutex_init(counter-mutex, NULL); } void counter_increment(ThreadSafeCounter* counter) { pthread_mutex_lock(counter-mutex); counter-value; pthread_mutex_unlock(counter-mutex); } int counter_get(ThreadSafeCounter* counter) { pthread_mutex_lock(counter-mutex); int value counter-value; pthread_mutex_unlock(counter-mutex); return value; }6. 线程池实现直接创建销毁线程开销很大线程池是更好的选择。下面是一个简单线程池的实现框架typedef struct { void (*function)(void*); void* argument; } Task; typedef struct { Task* queue; int queue_size; int head; int tail; int count; pthread_mutex_t lock; pthread_cond_t not_empty; pthread_cond_t not_full; } TaskQueue; typedef struct { pthread_t* threads; int thread_count; TaskQueue* queue; int shutdown; } ThreadPool; // 初始化线程池 ThreadPool* thread_pool_init(int thread_count, int queue_size) { ThreadPool* pool malloc(sizeof(ThreadPool)); // 初始化代码... return pool; } // 添加任务 void thread_pool_add_task(ThreadPool* pool, void (*function)(void*), void* arg) { pthread_mutex_lock(pool-queue-lock); // 等待队列有空位 while(pool-queue-count pool-queue-queue_size !pool-shutdown) { pthread_cond_wait(pool-queue-not_full, pool-queue-lock); } if(pool-shutdown) { pthread_mutex_unlock(pool-queue-lock); return; } // 添加任务到队列 pool-queue-queue[pool-queue-tail].function function; pool-queue-queue[pool-queue-tail].argument arg; pool-queue-tail (pool-queue-tail 1) % pool-queue-queue_size; pool-queue-count; pthread_cond_signal(pool-queue-not_empty); pthread_mutex_unlock(pool-queue-lock); } // 工作线程函数 void* worker_thread(void* arg) { ThreadPool* pool (ThreadPool*)arg; while(1) { pthread_mutex_lock(pool-queue-lock); // 等待任务 while(pool-queue-count 0 !pool-shutdown) { pthread_cond_wait(pool-queue-not_empty, pool-queue-lock); } if(pool-shutdown pool-queue-count 0) { pthread_mutex_unlock(pool-queue-lock); pthread_exit(NULL); } // 取出任务 Task task pool-queue-queue[pool-queue-head]; pool-queue-head (pool-queue-head 1) % pool-queue-queue_size; pool-queue-count--; pthread_cond_signal(pool-queue-not_full); pthread_mutex_unlock(pool-queue-lock); // 执行任务 (task.function)(task.argument); } return NULL; }这个线程池实现包含了任务队列、工作线程管理和基本的同步机制可以根据实际需求进行扩展。7. 性能优化与调试技巧多线程程序的性能优化和调试有其特殊性下面分享一些实用技巧7.1 锁粒度控制锁的粒度太大锁住太多代码或数据会降低并发性太小则增加锁开销。一个好的经验法则是锁持续时间不超过必要的操作锁保护的数据刚好满足需求读多写少时考虑读写锁// 粗粒度锁 - 不推荐 pthread_mutex_lock(global_lock); // 大量不相关的操作 pthread_mutex_unlock(global_lock); // 细粒度锁 - 更好 pthread_mutex_lock(data1_lock); // 只操作data1 pthread_mutex_unlock(data1_lock); pthread_mutex_lock(data2_lock); // 只操作data2 pthread_mutex_unlock(data2_lock);7.2 避免锁争用锁争用是性能杀手可以通过以下方式减少使用原子操作替代简单锁采用无锁数据结构分区锁将数据分到不同锁保护的区域// 使用原子操作替代锁 #include stdatomic.h atomic_int counter ATOMIC_VAR_INIT(0); void increment() { atomic_fetch_add(counter, 1); }7.3 调试工具Linux下常用的多线程调试工具gdb支持多线程调试常用命令info threads查看所有线程thread n切换到线程nthread apply all bt查看所有线程的调用栈Valgrind检测内存错误和竞态条件valgrind --toolhelgrind ./your_programstrace/ltrace跟踪系统调用和库函数调用perf性能分析工具perf stat ./your_program perf record ./your_program perf report7.4 常见陷阱信号处理信号是发送给整个进程的但哪个线程执行信号处理函数是不确定的。应该尽量避免在多线程程序中使用信号使用专门的信号处理线程通过pthread_sigmask屏蔽其他线程的信号考虑使用signalfd将信号转换为文件描述符事件errno问题errno是线程安全的但下面的代码有问题if(some_call() -1) { // 这里可能被其他线程修改errno printf(Error: %s\n, strerror(errno)); }应该立即保存errnoif(some_call() -1) { int saved_errno errno; printf(Error: %s\n, strerror(saved_errno)); }fork与线程在多线程程序中调用fork只有调用fork的线程被复制到子进程这可能导致死锁其他线程持有的锁在子进程中永远不会被释放。解决方案避免在多线程程序中使用fork如果必须使用fork后立即调用exec使用pthread_atfork注册处理函数8. 现代C中的线程管理虽然本文主要讨论POSIX线程但现代CC11及以上提供了更高级的线程支持#include iostream #include thread #include mutex #include condition_variable #include atomic #include vector std::mutex mtx; std::condition_variable cv; bool ready false; std::atomicint counter(0); void worker(int id) { std::unique_lockstd::mutex lock(mtx); cv.wait(lock, []{ return ready; }); std::cout Worker id started\n; counter; } int main() { std::vectorstd::thread threads; for(int i 0; i 5; i) { threads.emplace_back(worker, i); } { std::lock_guardstd::mutex lock(mtx); ready true; } cv.notify_all(); for(auto t : threads) { t.join(); } std::cout Counter: counter \n; return 0; }C线程库的优点类型安全RAII风格管理锁、线程等更高级的抽象future/promise等可移植性更好但在性能关键或需要精细控制的场景下POSIX线程仍然是必要的。
企业数字化 ERP 产品动态
相关推荐
UML图解析:软件工程中的可视化建模工具 1. UML图概述:软件工程的通用语言 统一建模语言(UML)是软件工程领域的事实标准,它用图形化的方式描述系统结构和行为。我第一次接触UML是在2005年参与银行系统重构时,当时项目组用类图梳理了上千个业务实体关系&#x… · 2026/11/10 1:57:34
汇编LOOP指令CX高位未清零导致无限循环的调试陷阱 简介:本资源是北京交通大学《汇编与接口》课程配套的Debug调试实验教学材料,面向计算机专业本科生及汇编语言初学者,聚焦底层程序调试能力培养。实验以经典闰年判断程序leapYear.exe为载体,系统覆盖编译链接流程、Debug单步执行&a… · 2026/11/10 19:36:20
用友NC65安装避坑指南:Oracle 11g R2实操配置与建库故障排查 简介:本资源是一份面向用友NC65初学者与实施顾问的实操型安装指南,专为自我学习、沙箱测试及NCC高版本环境搭建设计,有效解决数据库配置、中间件部署与系统初始化等关键实施难点。压缩包为单文件DOC文档(933KB)&#x… · 2026/11/3 18:11:07
有电工证容易出国吗怎么报名全攻略,别再被中介骗了 有电工证容易出国吗怎么报名全攻略,别再被中介骗了 你是不是也这样,想考个电工证,但不知道去哪报名,怕被中介坑?别急,今天我就用10年经验告诉你, 有电工证容易出国吗 这个问题其实要分情况看,而且 怎么报名… · 2026/11/10 19:35:23
电工证可以申请延期吗?考前押题帮你稳过考试 电工证可以申请延期吗?考前押题帮你稳过考试 现在找工作动不动就要求有电工证,尤其是刚毕业的应届生,连面试机会都难争取到。如果你也正在为电工证发愁,特别是担心证书到期了怎么办,那这篇内容就非常关键。今天咱们重点聊聊【电工证可以申请延期吗】这个… · 2026/11/10 19:35:23
电工证查询 全国联网怎么查?网上查询超简单! 电工证查询 全国联网怎么查?网上查询超简单! 之前考的证在外省能不能转过来?这是很多在外地打工、或者跨省工作的朋友最关心的问题。特别是像你这样在驻马店搞施工的企业负责人,手下工人来自五湖四海,证照管理是个大头。现在好消息来了, 电工证查询… · 2026/11/10 19:35:23
有电工证容易出国吗?电工证在哪考最靠谱 有电工证容易出国吗?电工证在哪考最靠谱 你是不是也跟很多人一样,拿到电工证后,脑子里就开始琢磨:“有电工证容易出国吗?”其实这个问题,很多人在考完证后都会问,但真正能给出靠谱答案的却不多。更让人心烦的是,不知道去哪报名怕被中介坑,一不小心就… · 2026/11/10 19:35:23
住建部电工证在哪办理 2026河南最新政策下怎么应对实操考试焦虑 住建部电工证在哪办理 2026河南最新政策下怎么应对实操考试焦虑 实操考试心里没底怕挂科,这几乎是每位准备考电工证的建筑工人都会有的想法。特别是2026河南最新政策对特种作业操作证的要求更加严格,从报考条件到实操考试,每一个环节都成了大家关… · 2026/11/10 19:35:23
榆林电工证报名入口到期换证流程 想考榆林电工证却怕考不过白交培训费?上岗必备干货来了! 怕考不过白交培训费?榆林电工证报名入口找对了,上岗必备的电工证拿到手,不仅让你在工地、厂房、工地这些地方能顺利开工,还能帮你把工资提上去。但很多人因为没经验、没准备,结果一考就挂,钱白… · 2026/11/10 19:34:23
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/10 2:16:10
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/10 17:46:39
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/10 17:11:43
丹寨电工证怎么考?内部消息透露报考条件和流程 丹寨电工证怎么考?内部消息透露报考条件和流程 急需特种作业证才能上岗找工作,这是很多电工朋友最真实的焦虑。尤其是像丹寨电工证这样的证书,直接影响你的职业发展和薪资待遇。但你知道吗?很多人都在报考过程中踩坑,有的甚至花了几千块最后白忙一场。今… · 2026/11/10 0:00:13
成都建筑电工证办理郑州报考避坑指南 成都建筑电工证办理郑州报考避坑指南 证书过期了不知道怎么复审延期?别慌!这篇文章手把手带你解决成都建筑电工证办理的难题,顺便避掉郑州报考的那些坑。不管你是刚拿到证的新手,还是准备复审的老电工,这波干货都别错过。… · 2026/11/10 0:00:13
梅州低压电工证怎么考?怕被中介坑?手把手教你全国通用流程 梅州低压电工证怎么考?怕被中介坑?手把手教你全国通用流程 你是不是正为梅州低压电工证发愁?不知道去哪报名,怕被中介骗钱,又担心证书是不是全国通用?其实,这些顾虑都是正常人会有的。今天我就从一个在开封房建工程一线干了几年的电工角度,手把手教你… · 2026/11/10 0:00:13
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/9 11:23:35
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 19:22:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 17:00:47