线程池触发竞态条件且子线程未执行问题求助
线程池代码问题解答
Q1:代码是否存在问题?执行时出现竞态条件
你的最小复现示例中输出重复数字,根本原因不是线程池内部的竞态,而是任务参数传递逻辑错误:
你在主线程循环中传递&idx作为任务参数,所有任务共享同一个idx的内存地址。主线程会快速递增idx,当子线程执行func读取该地址的值时,idx已经被主线程修改多次,甚至多个子线程可能同时读取到同一个被修改后的值,自然会出现重复输出。
解决参数传递问题的方案
为每个任务分配独立内存存储当前idx值,避免共享地址:
// 修改main中的循环逻辑 while (1) { int *num = malloc(sizeof(int)); *num = idx; tp.add_job(func, num); idx++; }
同时在任务函数中释放内存,避免泄漏:
void func(void* arg) { int num = *(int*)arg; printf("%d\n", num); free(arg); // 释放分配的内存 usleep(100); }
线程池自身的潜在问题
is_shutdown未声明为volatile,也无同步保护:主线程在析构时设置is_shutdown=1,子线程可能因CPU缓存优化看不到该修改,无法正确退出循环。建议改为volatile int is_shutdown,或访问时加互斥锁。- 析构函数存在时序问题:调用
sem_post后直接销毁互斥锁和信号量,此时子线程可能仍在运行,会引发未定义行为。需等待所有子线程退出后再销毁同步原语(可通过原子变量统计活跃线程数、配合条件变量实现)。
Q2:主线程不加sleep()时子线程未执行的原因
这是Linux线程调度的正常现象:
你的主线程是无限循环的密集计算任务(不停调用add_job和递增idx),会持续占用CPU时间片。Ubuntu默认采用SCHED_OTHER普通分时调度策略,同优先级线程下,主线程未主动放弃CPU(如调用sleep、usleep等阻塞调用)时,子线程很难抢到调度时间片,看起来就像未执行。
验证方法:在主线程循环中加入usleep(1)这类微小延迟,主动让出CPU,子线程即可正常执行任务。
原线程池头文件(thread_pool.h)
//this is thread_pool.h #ifndef _THREAD_POLL_H_ #define _THREAD_POLL_H_ #include <list> #include <vector> #include <unistd.h> // #include "locker.h" #include <pthread.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <semaphore.h> class thread_pool { private: struct task { void (*fun)(void*); void* arg; }; private: int size; //the num of working threads pthread_mutex_t lock; //a mutex sem_t sem; //semaphore to indicate the num of jobs std::list<task> tasks; std::vector<pthread_t> threads; const int default_size = 16; int is_shutdown; public: thread_pool(); thread_pool(int num); ~thread_pool(); void add_job(void (*fun)(void*), void* arg); private: static void* work(void* arg); //the working threads' call back void run(); //the actual function that work() calls }; #endif
原线程池实现(thread_pool.cpp)
//this is thread_pool.cpp #include "thread_pool.h" thread_pool::thread_pool(int num) : size(num), is_shutdown(0) { if (num <= 0) { fprintf(stderr, "the num of working threads is incorrect\n"); exit(1); } pthread_mutex_init(&lock, NULL); sem_init(&sem, 0, 0); threads.resize(num); pthread_mutex_lock(&lock); for (int i = 0; i < num; i++) { pthread_create(&threads[i], NULL, work, this); // printf("thread %d is created\n", threads[i]); pthread_detach(threads[i]); } pthread_mutex_unlock(&lock); } thread_pool::thread_pool() : thread_pool(default_size) {} thread_pool::~thread_pool() { is_shutdown = 1; for (int i = 0; i < size; i++) sem_post(&sem); pthread_mutex_destroy(&lock); sem_destroy(&sem); } void* thread_pool::work(void* arg) { thread_pool* pool = (thread_pool*)arg; pool->run(); return pool; } void thread_pool::run() { while (true) { sem_wait(&sem); if (is_shutdown) { break; } pthread_mutex_lock(&lock); if (tasks.empty()) { pthread_mutex_unlock(&lock); continue; } // printf("thread %d run\n", pthread_self()); task tmp = tasks.front(); tasks.pop_front(); pthread_mutex_unlock(&lock); tmp.fun(tmp.arg); } } void thread_pool::add_job(void (*fun)(void*), void* arg) { pthread_mutex_lock(&lock); task tmp; tmp.fun = fun; tmp.arg = arg; tasks.push_back(tmp); sem_post(&sem); pthread_mutex_unlock(&lock); }
原复现代码(main.cpp)
#include <stdio.h> #include "thread_pool.h" int idx = 0; void func(void* arg) { printf("%d\n", *(int*)arg); usleep(100); } int main() { thread_pool tp(8); while (1) { tp.add_job(func, (void*)&idx); idx++; } }
内容的提问来源于stack exchange,提问作者Torch
相关产品推荐
相关产品推荐

