聊天服务器——线程池

线程池

前缀知识

std::shared_ptr

std::shared_ptr是在c++11中引入的一种智能指针,其特点是它所指向的资源具有共享性,即多个shared_ptr可以指向同一份资源。在c++中使用shared_ptr需要包含<memory>头文件。

创建方式

shared_ptr<Investment> sp(newInvestment());                   //new
​
shared_ptr<Investment> sp1 = std::make_shared<Investment>();  //make_shared
 
shared_ptr<Investment> sp2 = sp1;                             //拷贝

std::shared_ptr 是一种智能指针,它能够记录多少个 shared_ptr 共同指向一个对象,从而消除显示的调用 delete,当引用计数变为零的时候就会将对象自动删除。 std::shared_ptr 可以通过 get() 方法来获取原始指针,通过 reset() 来减少一个引用计数, 并通过use_count()来查看一个对象的引用计数。

shared_ptr的循环引用会造成内存泄漏

struct Node
{
    int _val;
    std::shared_ptr<Node> _prev;
    std::shared_ptr<Node> _next;
 
    ~Node()
    {
        std::cout << "~Node()" << std::endl;
    }
};
 
int main()
{
    std::shared_ptr<Node> n1(new Node);
    std::shared_ptr<Node> n2(new Node);
 
    n1->_next = n2;
    n2->_prev = n1;
 
    return 0;
}

std::thread

每一个 C++11 程序都包含一个主线程即 main() 函数,在 C++11 中可以通过创建 std::thread 对象来创建新的线程,每个 std::thread 对象都可以与一个线程相关联。

需要引用的头文件:

#include <thread>

调用 thread 将立即同时开始执行这个新建立的线程之后 main() 的主线程也会继续执行

(1)get_id(): 取得目前的线程 id,回传一个为 std::thread::id 的类型 (2)joinable(): 检查是否可join (3)join(): 等待线程完成 (4)detach(): 与该线程分离,一旦该线程执行完后它所分配的资源会被释放 (5)native_handle(): 取得平台原生的native handle

#include <iostream>
#include <thread>
​
void foo() {
    std::cout << "foo\n";
}
​
void bar(int x) {
    std::cout << "bar\n";
}
​
int main() {
    std::thread t1(foo); // 建立一个新线程且执行 foo 函数
    std::thread t2(bar, 0); // 建立一個新线程且执行 bar 函数
    std::cout << "main, foo and bar now execute concurrently...\n"; // synchronize threads
​
    std::cout << "sleep 1s\n";
    std::this_thread::sleep_for(std::chrono::seconds(1));
​
    std::cout << "join t1\n";
    t1.join(); // 等待 t1 线程结束
    std::cout << "join t2\n";
    t2.join(); // 等待 t2 线程结束
​
    std::cout << "foo and bar completed.\n";
​
    return 0;
}
​

在 main 主线程建立 t1 线程后,主线程便继续往下执行,如果主线程需要等 t1 执行完毕后才能继续执行的话就需要使用 join,即等待 t1 线程执行完 foo 后主先线程才能继续执行,否则主线程会一直阻塞在 join 这一行

承上例,如果主线程不想等或是可以不用等待 t1 线程的话。就可以使用 detach 来让 t1 线程分离,接着主线程就可以继续执行,t1线程也在继续执行,在整个程序结束前最好养成好习惯确保所有子线程都已执行完毕,因为在 linux 系统如果主线程执行结束还有子线程在执行的话会报错。

CRITICAL_SECTION 临界区

CRITICAL_SECTION不是针对于资源的,而是针对于不同线程间的代码段的,我们能够用它来进行所谓资源的“锁定”,其实是因为我们在任何访问共享资源的地方都加入了EnterCriticalSectionLeaveCriticalSection语句,使得同一时间只能够有一个线程的代码段访问到该共享资源而已(其它想访问该资源的代码段不得不等待)。

初始化临界区

::InitializeCriticalSection

::CreateSemaphore 创建原子数

线程

bind需要头文件 #include <functional>

把一个成员函数变成全局静态函数

头文件:

#pragma once
#include <thread>
#include <memory>
​
class CThread
{
public:
    CThread();
    virtual ~CThread();
​
    CThread(const CThread& rhs) = delete;
    CThread& operator=(const CThread& rhs) = delete;
​
    void Start();
    virtual void Stop() = 0;
​
    void Join();
​
protected:
    virtual void Run() = 0;
​
private:
    void ThreadProc();
​
protected:
    bool                            m_bStop{ false };
​
private:
    std::shared_ptr<std::thread>    m_spThread;
};

源文件

#include "stdafx.h"
#include "Thread.h"
#include <functional>//bind
​
CThread::CThread()
{
​
}
​
CThread::~CThread()
{
​
}
​
void CThread::Start()
{
    m_spThread.reset(new std::thread(std::bind(&CThread::ThreadProc, this)));
}
​
void CThread::ThreadProc()
{
    Run();
}
​
void CThread::Join()
{
    m_spThread->join();
}

线程池

线程池的工作原理是这样的:

线程池主要有线程信息数组、任务信息数组。线程信息临界区和任务信息临界区。

线程池初始化:

初始化的时候有线程数量,首先创造临界区。然后进入线程信息临界区,根据线程数量,创造线程

hThread = (HANDLE)(ULONG_PTR)_beginthreadex(NULL, 0,  
    &ThreadProc, lpThreadInfo, CREATE_SUSPENDED, (UINT*)&dwThreadID);

注意,创造线程的时候,这里线程是挂起的CREATE_SUSPENDED。也就是说线程还没开始运行

运行的线程函数是ThreadProc,传递的参数是线程信息 TP_THREAD_INFO

线程信息填的是创建线程的Handle和线程池指针。

创建好线程后,就把线程放入到线程信息数组里。

线程池添加任务:

就是进入任务信息临界区,然后把任务放入到任务信息临界区

执行的线程函数:

ThreadProc,执行的线程函数就是会有一个无限循环。

在这个无限循环里,会从任务信息数组里取出任务,然后去执行这个任务。

先是进入任务信息数组临界区,然后遍历这个数组,看看哪个任务还没有被执行,然后取出这个任务,把这个任务设为已执行。然后退出遍历。开始执行这个任务。

执行完任务之后,就再进入到任务信息临界区,然后遍历数组,找到这个任务在数组中的位置,然后删除掉这个任务。

之后又进入到大循环里。

头文件

#pragma once
​
#include <vector>
​
class CThreadPoolTask
{
public:
    CThreadPoolTask();
    virtual ~CThreadPoolTask();
​
public:
    virtual BOOL IsRunning();
    virtual void SetRunning(BOOL bRunning);
    virtual int Run();
    virtual int Stop();                     //让正在运行的任务结束的接口(必须实现)
    virtual void TaskFinish();              //重载这个函数让已经结束的任务提供一个清理资源的机会
​
private:
    BOOL m_bRunning;
};
​
struct TP_THREAD_INFO       // 线程信息结构
{
    HANDLE hThread;
    LPARAM lParam;
};
​
class CThreadPool
{
public:
    CThreadPool();
    ~CThreadPool();
​
public:
    BOOL Init(int nThreadNums);
    void UnInit();
    BOOL AddTask(CThreadPoolTask* lpTask);
    BOOL InsertTask(int nIndex, CThreadPoolTask* lpTask);
    void RemoveTask(CThreadPoolTask* lpTask);
    void RemoveAllTask();
​
private:
    static UINT APIENTRY ThreadProc(LPVOID lpParam);    // 线程函数
​
private:
    std::vector<TP_THREAD_INFO*> m_arrThreadInfo;       // 线程信息数组
    std::vector<CThreadPoolTask*> m_arrTask;            // 任务信息数组
    
    CRITICAL_SECTION m_csThreadInfo;                    // 线程信息临界区
    CRITICAL_SECTION m_csTask;                          // 任务信息临界区
​
    HANDLE m_hSemaphore_Task;                           // 任务通知信号量
    HANDLE m_hEvent_Exit;                               // 退出线程通知事件句柄
    HANDLE m_hEvent_ExitAll;                            // 所有线程均退出完成通知事件句柄
​
    long m_lThreadNums;                                 // 线程总数
    long m_lRunningThreadNums;                          // 正在工作中的线程计数
};

源文件

#include "stdafx.h"
#include "ThreadPool.h"
​
CThreadPoolTask::CThreadPoolTask()
{
    m_bRunning = FALSE;
}
​
CThreadPoolTask::~CThreadPoolTask()
{
​
}
​
BOOL CThreadPoolTask::IsRunning()
{
    return m_bRunning;
}
​
void CThreadPoolTask::SetRunning(BOOL bRunning)
{
    m_bRunning = bRunning;
    if (!m_bRunning)
        TaskFinish();
}
​
int CThreadPoolTask::Run()
{
    return 0;
}
​
int CThreadPoolTask::Stop()
{
    return 0;
}
​
void CThreadPoolTask::TaskFinish()
{
    return;
}
​
CThreadPool::CThreadPool()
{
    memset(&m_csThreadInfo, 0, sizeof(CRITICAL_SECTION));
    memset(&m_csTask, 0, sizeof(CRITICAL_SECTION));
​
    m_hSemaphore_Task = NULL;
    m_hEvent_Exit = NULL;
    m_hEvent_ExitAll = NULL;
​
    m_lThreadNums = 0;
    m_lRunningThreadNums = 0;
}
​
CThreadPool::~CThreadPool()
{
    
}
​
BOOL CThreadPool::Init(int nThreadNums)
{
    TP_THREAD_INFO* lpThreadInfo;
    HANDLE hThread;
    DWORD dwThreadID;
​
    if (nThreadNums <= 0)
        return FALSE;
​
    m_lThreadNums = 0;
    m_lRunningThreadNums = 0;
​
    ::InitializeCriticalSection(&m_csThreadInfo);
    ::InitializeCriticalSection(&m_csTask);
​
    m_hSemaphore_Task = ::CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
    m_hEvent_Exit = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    m_hEvent_ExitAll = ::CreateEvent(NULL, FALSE, FALSE, NULL);
​
    ::EnterCriticalSection(&m_csThreadInfo);
    for (int i = 0; i < nThreadNums; i++)
    {
        lpThreadInfo = new TP_THREAD_INFO;
        if (lpThreadInfo != NULL)
        {
            hThread = (HANDLE)(ULONG_PTR)_beginthreadex(NULL, 0,  
                &ThreadProc, lpThreadInfo, CREATE_SUSPENDED, (UINT*)&dwThreadID);
            //开启一个线程,CREATE_SUSPENDED让线程等一会儿再运行
            if (hThread != NULL)
            {
                lpThreadInfo->hThread = hThread;
                lpThreadInfo->lParam = (LPARAM)this;
                m_arrThreadInfo.push_back(lpThreadInfo);
​
                ::ResumeThread(hThread);    //恢复一个被暂停的线程
            }
            else
            {
                delete lpThreadInfo;
            }
        }
    }
    ::LeaveCriticalSection(&m_csThreadInfo);
​
    return TRUE;
}
​
void CThreadPool::UnInit()
{
    RemoveAllTask();
​
    ::SetEvent(m_hEvent_Exit);
​
    ::WaitForSingleObject(m_hEvent_ExitAll, 30*1000);   // 等待所有线程退出完毕,超时时间设为30秒
​
    ::EnterCriticalSection(&m_csThreadInfo);
    for (int i = 0; i < (int)m_arrThreadInfo.size(); i++)
    {
        TP_THREAD_INFO* lpThreadInfo = m_arrThreadInfo[i];
        if (lpThreadInfo != NULL)
        {
            ::CloseHandle(lpThreadInfo->hThread);
            DWORD dwExitCode = 0;
            ::GetExitCodeThread(lpThreadInfo->hThread, &dwExitCode);
            if (STILL_ACTIVE == dwExitCode)
                ::TerminateThread(lpThreadInfo->hThread, 0);
            delete lpThreadInfo;
        }
    }
    m_arrThreadInfo.clear();
    ::LeaveCriticalSection(&m_csThreadInfo);
​
    ::EnterCriticalSection(&m_csTask);
    m_arrTask.clear();
    ::LeaveCriticalSection(&m_csTask);
​
    if (m_hSemaphore_Task != NULL)
    {
        ::CloseHandle(m_hSemaphore_Task);
        m_hSemaphore_Task = NULL;
    }
​
    if (m_hEvent_Exit != NULL)
    {
        ::CloseHandle(m_hEvent_Exit);
        m_hEvent_Exit = NULL;
    }
​
    if (m_hEvent_ExitAll != NULL)
    {
        ::CloseHandle(m_hEvent_ExitAll);
        m_hEvent_ExitAll = NULL;
    }
​
    ::DeleteCriticalSection(&m_csThreadInfo);
    ::DeleteCriticalSection(&m_csTask);
}
​
BOOL CThreadPool::AddTask(CThreadPoolTask* lpTask)
{
    if (NULL == lpTask)
        return FALSE;
​
    ::EnterCriticalSection(&m_csTask);
    m_arrTask.push_back(lpTask);
    ::LeaveCriticalSection(&m_csTask);
​
    ::ReleaseSemaphore(m_hSemaphore_Task, 1, NULL);     // 增加任务通知信号量
​
    return TRUE;
}
​
BOOL CThreadPool::InsertTask(int nIndex, CThreadPoolTask* lpTask)
{
    if (NULL == lpTask)
        return FALSE;
​
    ::EnterCriticalSection(&m_csTask);
    m_arrTask.insert(m_arrTask.begin() + nIndex, lpTask);
    ::LeaveCriticalSection(&m_csTask);
​
    ::ReleaseSemaphore(m_hSemaphore_Task, 1, NULL);     // 增加任务通知信号量
​
    return TRUE;
}
​
void CThreadPool::RemoveTask(CThreadPoolTask* lpTask)
{
    if (NULL == lpTask)
        return;
​
    ::EnterCriticalSection(&m_csTask);
    for (int i = 0; i < (int)m_arrTask.size(); i++)
    {
        if (m_arrTask[i] == lpTask)
        {
            if (lpTask->IsRunning())
            {
                lpTask->Stop();
            }
            else
            {
                m_arrTask.erase(m_arrTask.begin() + i);
                lpTask->TaskFinish();
            }
            break;
        }
    }
    ::LeaveCriticalSection(&m_csTask);
}
​
void CThreadPool::RemoveAllTask()
{
    ::EnterCriticalSection(&m_csTask);
    for (int i = (int)m_arrTask.size() - 1; i >= 0; i--)
    {
        CThreadPoolTask* lpTask = m_arrTask[i];
        if (lpTask != NULL)
        {
            if (lpTask->IsRunning())
            {
                lpTask->Stop();
            }
            else
            {
                m_arrTask.erase(m_arrTask.begin() + i);
                lpTask->TaskFinish();
            }
        }
    }
    ::LeaveCriticalSection(&m_csTask);
}
​
// 线程函数
UINT CThreadPool::ThreadProc(LPVOID lpParam)
{
    TP_THREAD_INFO* lpThreadInfo;
    CThreadPool* lpThis;
    CThreadPoolTask* lpTask,* lpTempTask;
    HANDLE hWaitEvent[2];
    DWORD dwIndex;
​
    lpThreadInfo = (TP_THREAD_INFO*)lpParam;
    if (NULL == lpThreadInfo)
        return 0;
​
    lpThis = (CThreadPool*)lpThreadInfo->lParam;
    if (NULL == lpThis)
        return 0;
​
    ::InterlockedIncrement(&lpThis->m_lThreadNums);     // 增加线程计数
​
    hWaitEvent[0] = lpThis->m_hEvent_Exit;
    hWaitEvent[1] = lpThis->m_hSemaphore_Task;
​
    for (;;)
    {
        dwIndex = ::WaitForMultipleObjects(2, hWaitEvent, FALSE, INFINITE);
        //填true就是所有事件发出信号,那么就响应。填false就是任何事件发出信号,那么就响应。
        
        if (dwIndex == WAIT_OBJECT_0)   // 接到退出线程通知事件
            break;
​
        lpTask = NULL;
​
        ::EnterCriticalSection(&lpThis->m_csTask);  // 取任务
        for (int i = 0; i < (int)lpThis->m_arrTask.size(); i++)
        {
            lpTempTask = lpThis->m_arrTask[i];
            if (lpTempTask != NULL)
            {
                if (!lpTempTask->IsRunning())
                {
                    lpTempTask->SetRunning(TRUE);
                    lpTask = lpTempTask;
                    break;
                }
            }
        }
        ::LeaveCriticalSection(&lpThis->m_csTask);
​
        if (lpTask != NULL)     // 有任务
        {
            ::InterlockedIncrement(&lpThis->m_lRunningThreadNums);  // 增加正在工作中线程计数
​
            //有任务取任务,并执行任务,执行完就把任务删除了
            lpTask->Run();
            
            ::EnterCriticalSection(&lpThis->m_csTask);  // 删除任务
            for (int i = 0; i < (int)lpThis->m_arrTask.size(); i++)
            {
                lpTempTask = lpThis->m_arrTask[i];
                if (lpTempTask == lpTask)
                {
                    lpThis->m_arrTask.erase(lpThis->m_arrTask.begin() + i);
                    break;
                }
            }
            lpTask->SetRunning(FALSE);
            ::LeaveCriticalSection(&lpThis->m_csTask);
​
            ::InterlockedDecrement(&lpThis->m_lRunningThreadNums);  // 减少正在工作中线程计数
        }
    }
​
    ::InterlockedDecrement(&lpThis->m_lThreadNums);     // 减少线程计数
​
    if (lpThis->m_lThreadNums <= 0)     // 所有线程都结束完毕,置事件句柄有信号
        ::SetEvent(lpThis->m_hEvent_ExitAll);
​
    return 0;
}
  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值