【转】一个简单的线程池(c++版)

本文介绍了一个基于Windows平台的线程池实现方法,包括如何创建、调整线程池大小及调用线程池执行任务等核心功能。此外,还提供了具体的使用案例,帮助读者更好地理解和应用线程池。
#ifndef _ThreadPool_H_
#define _ThreadPool_H_
#pragma warning(disable: 4530)
#pragma warning(disable: 4786)
#include 
<cassert>
#include 
<vector>
#include 
<queue>
#include 
<windows.h>
class ThreadJob  //工作基类
{
public:
    
//供线程池调用的虚函数
    virtual void DoJob(void *pPara) = 0;
};
class ThreadPool
{
public:
    
//dwNum 线程池规模
    ThreadPool(DWORD dwNum = 4) : _lThreadNum(0), _lRunningNum(0)
    {
        InitializeCriticalSection(
&_csThreadVector);
        InitializeCriticalSection(
&_csWorkQueue);
        _EventComplete 
= CreateEvent(0falsefalse, NULL);
        _EventEnd 
= CreateEvent(0truefalse, NULL);
        _SemaphoreCall 
= CreateSemaphore(00,  0x7FFFFFFF, NULL);
        _SemaphoreDel 
=  CreateSemaphore(00,  0x7FFFFFFF, NULL);
        assert(_SemaphoreCall 
!= INVALID_HANDLE_VALUE);
        assert(_EventComplete 
!= INVALID_HANDLE_VALUE);
        assert(_EventEnd 
!= INVALID_HANDLE_VALUE);
        assert(_SemaphoreDel 
!= INVALID_HANDLE_VALUE);
        AdjustSize(dwNum 
<= 0 ? 4 : dwNum);
    }
    
~ThreadPool()
    {
        DeleteCriticalSection(
&_csWorkQueue);
        CloseHandle(_EventEnd);
        CloseHandle(_EventComplete);
        CloseHandle(_SemaphoreCall);
        CloseHandle(_SemaphoreDel);
        vector
<ThreadItem*>::iterator iter;
        
for(iter = _ThreadVector.begin(); iter != _ThreadVector.end(); iter++)
        {
            
if(*iter)
                delete 
*iter;
        }
        DeleteCriticalSection(
&_csThreadVector);
    }
    
//调整线程池规模
    int AdjustSize(int iNum)
    {
        
if(iNum > 0)
        {
            ThreadItem 
*pNew;
            EnterCriticalSection(
&_csThreadVector);
            
for(int _i=0; _i<iNum; _i++)
            {
                _ThreadVector.push_back(pNew 
= new ThreadItem(this));
                assert(pNew);
                pNew
->_Handle = CreateThread(NULL, 0, DefaultJobProc, pNew, 0, NULL);
                assert(pNew
->_Handle);
            }
            LeaveCriticalSection(
&_csThreadVector);
        }
        
else
        {
            iNum 
*= -1;
            ReleaseSemaphore(_SemaphoreDel,  iNum 
> _lThreadNum ? _lThreadNum : iNum, NULL);
        }
        
return (int)_lThreadNum;
    }
    
//调用线程池
    void Call(void (*pFunc)(void  *), void *pPara = NULL)
    {
        assert(pFunc);
        EnterCriticalSection(
&_csWorkQueue);
        _JobQueue.push(
new JobItem(pFunc, pPara));
        LeaveCriticalSection(
&_csWorkQueue);
        ReleaseSemaphore(_SemaphoreCall, 
1, NULL);
    }
    
//调用线程池
    inline void Call(ThreadJob * p, void *pPara = NULL)
    {
        Call(CallProc, 
new CallProcPara(p, pPara));
    }
    
//结束线程池, 并同步等待
    bool EndAndWait(DWORD dwWaitTime = INFINITE)
    {
        SetEvent(_EventEnd);
        
return WaitForSingleObject(_EventComplete, dwWaitTime) == WAIT_OBJECT_0;
    }
    
//结束线程池
    inline void End()
    {
        SetEvent(_EventEnd);
    }
    inline DWORD Size()
    {
        
return (DWORD)_lThreadNum;
    }
    inline DWORD GetRunningSize()
    {
        
return (DWORD)_lRunningNum;
    }
    
bool IsRunning()
    {
        
return _lRunningNum > 0;
    }
protected:
    
//工作线程
    static DWORD WINAPI DefaultJobProc(LPVOID lpParameter = NULL)
    {
        ThreadItem 
*pThread = static_cast<ThreadItem*>(lpParameter);
        assert(pThread);
        ThreadPool 
*pThreadPoolObj = pThread->_pThis;
        assert(pThreadPoolObj);
        InterlockedIncrement(
&pThreadPoolObj->_lThreadNum);
        HANDLE hWaitHandle[
3];
        hWaitHandle[
0= pThreadPoolObj->_SemaphoreCall;
        hWaitHandle[
1= pThreadPoolObj->_SemaphoreDel;
        hWaitHandle[
2= pThreadPoolObj->_EventEnd;
        JobItem 
*pJob;
        
bool fHasJob;
        
for(;;)
        {
            DWORD wr 
= WaitForMultipleObjects(3, hWaitHandle, false, INFINITE);
            
//响应删除线程信号
            if(wr == WAIT_OBJECT_0 + 1
                
break;
            
//从队列里取得用户作业
            EnterCriticalSection(&pThreadPoolObj->_csWorkQueue);
            
if(fHasJob = !pThreadPoolObj->_JobQueue.empty())
            {
                pJob 
= pThreadPoolObj->_JobQueue.front();
                pThreadPoolObj
->_JobQueue.pop();
                assert(pJob);
            }
            LeaveCriticalSection(
&pThreadPoolObj->_csWorkQueue);
            
//受到结束线程信号确定是否结束线程(结束线程信号&& 是否还有工作)
            if(wr == WAIT_OBJECT_0 + 2 && !fHasJob) 
                
break;
            
if(fHasJob && pJob)
            {
                InterlockedIncrement(
&pThreadPoolObj->_lRunningNum);
                pThread
->_dwLastBeginTime = GetTickCount();
                pThread
->_dwCount++;
                pThread
->_fIsRunning = true;
                pJob
->_pFunc(pJob->_pPara); //运行用户作业
                delete pJob;
                pThread
->_fIsRunning = false;
                InterlockedDecrement(
&pThreadPoolObj->_lRunningNum);
            }
        }
        
//删除自身结构
        EnterCriticalSection(&pThreadPoolObj->_csThreadVector);
    pThreadPoolObj
->_ThreadVector.erase(find(pThreadPoolObj->_ThreadVector.begin(), pThreadPoolObj->_ThreadVector.end(), pThread));
        LeaveCriticalSection(
&pThreadPoolObj->_csThreadVector);
        delete pThread;
        InterlockedDecrement(
&pThreadPoolObj->_lThreadNum);
        
if(!pThreadPoolObj->_lThreadNum)  //所有线程结束
            SetEvent(pThreadPoolObj->_EventComplete);
        
return 0;
    }
    
//调用用户对象虚函数
    static void CallProc(void *pPara)
    {
        CallProcPara 
*cp = static_cast<CallProcPara *>(pPara);
        assert(cp);
        
if(cp)
        {
            cp
->_pObj->DoJob(cp->_pPara);
            delete cp;
        }
    }
    
//用户对象结构
    struct CallProcPara 
    {
        ThreadJob
* _pObj;//用户对象
        void *_pPara;//用户参数
        CallProcPara(ThreadJob* p, void *pPara) : _pObj(p), _pPara(pPara) { };
    };
    
//用户函数结构
    struct JobItem
    {
        
void (*_pFunc)(void  *);//函数
        void *_pPara; //参数
        JobItem(void (*pFunc)(void  *= NULL, void *pPara = NULL) : _pFunc(pFunc), _pPara(pPara) { };
    };
    
//线程池中的线程结构
    struct ThreadItem
    {
        HANDLE _Handle; 
//线程句柄
        ThreadPool *_pThis;  //线程池的指针
        DWORD _dwLastBeginTime; //最后一次运行开始时间
        DWORD _dwCount; //运行次数
        bool _fIsRunning;
        ThreadItem(ThreadPool 
*pthis) : _pThis(pthis), _Handle(NULL), _dwLastBeginTime(0), _dwCount(0), _fIsRunning(false) { };
        
~ThreadItem()
        {
            
if(_Handle)
            {
                CloseHandle(_Handle);
                _Handle 
= NULL;
            }
        }
    };
    std::queue
<JobItem *> _JobQueue;  //工作队列
    std::vector<ThreadItem *>  _ThreadVector; //线程数据
    CRITICAL_SECTION _csThreadVector, _csWorkQueue; //工作队列临界, 线程数据临界
    HANDLE _EventEnd, _EventComplete, _SemaphoreCall, _SemaphoreDel;//结束通知, 完成事件, 工作信号,删除线程信号
    long _lThreadNum, _lRunningNum; //线程数, 运行的线程数
};
#endif //_ThreadPool_H_

使用说明1

调用方法使用时注意几点:

void threadfunc(void *p)
{
     YourClass
* yourObject = (YourClass*)    p;
 
//
}
 ThreadPool tp;
 
for(i=0; i<100; i++)
  tp.Call(threadfunc);
ThreadPool tp(
20);//20为初始线程池规模
 tp.Call(threadfunc, lpPara);

 

1. ThreadJob  没什么用,直接写线程函数吧。 

2. 线程函数(threadfunc)的入口参数void* 可以转成自定义的类型对象,这个对象可以记录下线程运行中的数据,并设置线程当前状态,以此与线程进行交互。

3. 线程池有一个EndAndWait函数,用于让线程池中所有计算正常结束。有时线程池中的一个线程可能要运行很长时间,怎么办?可以通过线程函数threadfunc的入口参数对象来处理,比如:在主线程中设置yourClass->cmd = 1,该线程就会自然结束。

class YourClass 
{
  
int cmd; // cmd = 1是上线程停止计算,正常退出。
};
threadfunc(
void* p) {
  YourClass
* yourObject = (YourClass*)p;
  
while (true) {
    
// do some calculation
    if (yourClass->cmd == 1)
      
break;
  }
}

 

使用说明2 

Code

 

class MyThreadJob : public ThreadJob //线程对象从ThreadJob扩展
{
public:
 
virtual void DoJob(void *p)//自定义的虚函数
 {
  
//.
 }
};
 MyThreadJob mt[
10];
 ThreadPool tp;
 
for(i=0; i<100 i++)
  tp.Call(mt 
+ i);//tp.Call(mt + i, para);
<think> 我们正在处理一个关于Python调用C++ DLL时出现低频崩溃的问题。用户提到了线程池,因此问题可能与多线程相关。结合用户提供的引用,我们可以推测崩溃可能涉及以下几个方面: 1. **跨语言调用问题**:Python和C++之间的接口可能存在问题,尤其是在传递数据或管理内存时。 2. **线程安全问题**:C++ DLL中的线程池可能没有正确处理多线程同步,导致竞态条件或死锁。 3. **资源管理问题**:如引用[2]所述,非托管代码(C++)和托管环境(Python)在内存管理上的差异可能导致问题,尤其是当C++代码没有正确管理内存时,而Python的垃圾回收机制可能与之冲突。 4. **消息循环问题**:引用[3]提到了WinMain中的消息循环,虽然这是Windows GUI程序的消息循环,但提醒我们检查是否在DLL中有类似的消息处理机制,或者是否有未处理的消息导致阻塞。 ### 解决步骤 1. **确保C++ DLL的线程安全**: - 检查线程池的代码,确保共享资源有适当的锁(如互斥锁)保护。 - 避免死锁:确保锁的获取和释放顺序一致,并且尽量缩短锁的持有时间。 - 使用线程安全的容器和数据结构。 2. **检查跨语言边界的内存管理**: - 确保在C++中分配的内存,如果需要在Python中释放,必须使用相同的分配器(例如,如果在C++中分配,则在C++中释放)。 - 避免在跨语言调用中传递复杂对象,尽量使用基本类型或简单的结构体。 - 如果传递指针,确保指针的生命周期足够长,不会被提前释放。 3. **调试与日志**: - 在C++ DLL中添加详细的日志,尤其是在线程池的任务开始、结束和异常处理部分。记录线程ID、任务状态等信息。 - 在Python端,使用`ctypes`或`cffi`调用DLL时,确保正确设置参数类型和返回类型,避免类型不匹配导致的崩溃。 4. **异常处理**: - 在C++代码中,确保捕获所有可能的异常,并将异常信息换为错误码返回给Python,避免异常传播到Python解释器导致崩溃。 - 在Python端,检查每次调用的返回值,并处理错误。 5. **检查资源泄漏**: - 使用工具(如Valgrind、Dr. Memory等)检查C++代码中的内存泄漏、句柄泄漏等。 - 确保线程池中的线程能够正确退出,避免线程堆积。 6. **检查GIL(全局解释器锁)问题**: - 当Python调用C++ DLL时,C++代码会释放GIL。如果C++代码回调Python函数,需要在回调前获取GIL,否则会导致崩溃。 - 使用`PyGILState_Ensure`和`PyGILState_Release`来管理GIL状态。 7. **测试与复现**: - 尝试在C++端编写测试程序,直接调用线程池,排除Python调用的问题。 - 在Python端增加调用频率,尝试提高崩溃的复现率,以便定位问题。 ### 示例:C++线程池简单实现(注意线程安全) 以下是一个简单C++线程池实现,注意使用互斥锁保护任务队列: ```cpp #include <vector> #include <queue> #include <thread> #include <mutex> #include <condition_variable> #include <functional> #include <atomic> class ThreadPool { public: ThreadPool(size_t threads) : stop(false) { for (size_t i = 0; i < threads; ++i) workers.emplace_back([this] { while (true) { std::function<void()> task; { std::unique_lock<std::mutex> lock(this->queue_mutex); this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); if (this->stop && this->tasks.empty()) return; task = std::move(this->tasks.front()); this->tasks.pop(); } task(); } }); } template<class F> void enqueue(F&& f) { { std::unique_lock<std::mutex> lock(queue_mutex); tasks.emplace(std::forward<F>(f)); } condition.notify_one(); } ~ThreadPool() { { std::unique_lock<std::mutex> lock(queue_mutex); stop = true; } condition.notify_all(); for (std::thread &worker : workers) worker.join(); } private: std::vector<std::thread> workers; std::queue<std::function<void()>> tasks; std::mutex queue_mutex; std::condition_variable condition; std::atomic<bool> stop; }; ``` ### 在Python中调用C++ DLL的注意事项 使用`ctypes`调用DLL时,确保正确声明函数原型,特别是涉及回调函数时: ```python from ctypes import * # 加载DLL mydll = cdll.LoadLibrary("mydll.dll") # 声明函数原型 mydll.start_thread_pool.argtypes = [] mydll.start_thread_pool.restype = None # 启动线程池 mydll.start_thread_pool() ``` 如果DLL中有回调函数,需要定义回调类型: ```python # 定义回调函数类型 CALLBACK_FUNC = CFUNCTYPE(None, c_int) # 设置回调 callback = CALLBACK_FUNC(py_callback_func) mydll.set_callback(callback) ``` 同时,注意回调函数中如果需要调用Python API,必须获取GIL: ```c // C++端回调函数,通过函数指针调用 void call_python_callback(void (*callback)(int), int value) { // 在调用Python回调前获取GIL PyGILState_STATE gstate = PyGILState_Ensure(); callback(value); // 调用Python函数 PyGILState_Release(gstate); } ``` ### 结论 低频崩溃问题通常难以定位,需要结合详细的日志和内存检测工具逐步排查。重点检查线程同步、内存管理和跨语言调用规范。确保C++代码在内存分配和线程同步上是安全的,并在跨语言调用时正确处理GIL。 §§相关问题§§ 1. 如何检测C++ DLL中的内存泄漏? 2. 在跨语言调用中如何管理GIL(全局解释器锁)? 3. 如何设计线程安全的线程池? </think> ### Python调用C++ DLL线程池低频崩溃的解决方案 低频崩溃问题通常由线程安全、资源管理或跨语言边界问题引起。以下是系统化的解决方案: #### 1. **线程池资源管理** - **检查资源泄漏**: ```cpp // C++ DLL示例:确保任务完成后释放资源 void ThreadTask() { try { // 业务逻辑... } catch (...) { // 捕获所有异常并记录 LogError("ThreadTask exception"); } // 显式释放资源 ReleaseResources(); } ``` 使用内存检测工具(如Valgrind或Dr.Memory)检查内存泄漏[^2]。 - **同步机制优化**: ```cpp std::mutex resource_mutex; void SafeResourceAccess() { std::lock_guard<std::mutex> lock(resource_mutex); // 访问共享资源 } ``` 避免过度加锁导致死锁,推荐使用`std::lock_guard`或`std::scoped_lock`(C++17)。 #### 2. **跨语言边界安全** - **数据类型换**: ```python # Python端正确指定参数类型 from ctypes import c_int, c_void_p, CDLL dll = CDLL("mypool.dll") dll.StartTask.argtypes = [c_int, c_void_p] # 明确参数类型 dll.StartTask.restype = c_int ``` 确保Python传递的参数类型与C++完全匹配(如避免`int`与`size_t`混用)。 - **生命周期管理**: ```cpp // C++导出函数:返回堆内存需明确释放规则 extern "C" __declspec(dllexport) char* GenerateData() { char* buf = new char[100]; return buf; // Python需调用对应的DeleteBuffer释放 } ``` Python端需配套释放函数: ```python dll.DeleteBuffer.argtypes = [c_void_p] dll.DeleteBuffer.restype = None ``` #### 3. **异常处理加固** - **C++内部捕获**: ```cpp __declspec(dllexport) void ThreadEntry() { try { // 线程主逻辑 } catch (const std::exception& e) { Log("Exception: " + std::string(e.what())); } catch (...) { Log("Unknown exception"); } } ``` 阻止异常跨DLL边界传播(C++异常跨模块传递易崩溃)[^1]。 - **Python端回调安全**: ```cpp // C++调用Python回调时锁定GIL PyGILState_STATE gstate = PyGILState_Ensure(); PyObject_Call(callback, args, NULL); PyGILState_Release(gstate); ``` #### 4. **线程池配置检查** - **负载均衡**: ```cpp // 限制最大线程数避免资源耗尽 ThreadPool pool(std::thread::hardware_concurrency() * 2); ``` 监控任务队列积压情况,拒绝过量任务提交。 - **优雅退出**: ```cpp pool.~ThreadPool(); // 析构时等待所有任务完成 ``` #### 5. **调试与诊断** - **崩溃日志收集**: - Windows:使用`SetUnhandledExceptionFilter`捕获minidump - Linux:通过`backtrace`生成堆栈信息 - **增量验证**: 1. 单独测试C++线程池(无Python调用) 2. 逐步增加跨语言调用复杂度 3. 压力测试:`for _ in range(10000): dll.SubmitTask()` #### 典型解决方案流程: ```mermaid graph TD A[崩溃发生] --> B[捕获minidump/堆栈] B --> C{分析崩溃点} C -->|资源竞争| D[加固锁机制] C -->|内存错误| E[检查跨语言数据类型] C -->|未处理异常| F[添加C++ try-catch块] D --> G[回归测试] E --> G F --> G G -->|仍崩溃| H[检查第三方依赖] H --> I[更新编译器/运行时库] ``` > **关键提示**:此类问题90%集中在: > (1) 跨语言传递了无效指针 > (2) 未保护共享资源的线程竞争 > (3) C++异常未被捕获导致栈展开错误[^3]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值