C++ 多线程 API 简介

    科技2022-07-11  86

    文章目录

    一、线程 API 介绍1、创建线程2、恢复线程3、等待信号量4、线程状态判定5、销毁线程 二、线程类的封装1、设计思路2、头文件设计3、接口实现4、接口分析 三、线程类的使用1、线程类继承2、线程类调用

    一、线程 API 介绍

    1、创建线程

    _beginthreadex

    unsigned long _beginthreadex( void *security, unsigned stack_size, unsigned(_stdcall *start_address)(void *), void *argilist, unsigned initflag, unsigned *threaddr ); 参数名参数类型含义securityvoid指针安全属性, 为NULL时表示默认安全性stack_size无符号整型线程的堆栈大小, 一般默认为0start_address函数地址线程回调函数,参数类型为 void*,传 NULL 会有异常抛出argilistvoid指针线程回调函数的参数,需要多个参数的时候传递结构体指针即可initflag无符号整型新线程的初始状态,0表示立即执行,CREATE_SUSPENDED 表示创建之后挂起,调用 ResumeThread 恢复threaddr线程ID地址用来接收线程ID,传 NULL 表示不使用

    返回值

    如果线程创建成功,返回一个该线程的句柄;如果创建失败,返回 0,并且置错误码 errno,错误码相关的可以参考:errno 简介;

    2、恢复线程

    ResumeThread

    DWORD ResumeThread( HANDLE hThread ); 参数名参数类型含义hThreadHANDLE需要恢复的线程句柄 如果在创建线程的时候,initflag 置上了标志位 CREATE_SUSPENDED,则代表线程创建后不会立即执行,需要调用 ResumeThread 进行线程恢复;

    返回值

    如果函数执行成功,则返回线程被挂起的次数; 返回值原因0线程没有被挂起1线程曾经被挂起,但是现在已经恢复>1线程还是处于挂起状态 如果函数执行失败,则返回 (DWORD) -1,具体错误码,通过 GetLastError 函数获取;

    3、等待信号量

    WaitForSingleObject

    DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds ); 参数名参数类型含义hHandleHANDLE等待信号的句柄,不仅仅是线程句柄,还可以是 信号量、进程、事件、控制台输入 等其它对象的句柄等等dwMillisecondsDWORD超时时间,如果这个参数是 INFINITE ,则忽略超时时间

    返回值

    这是一个阻塞函数,如果线程还在执行,这里是不会返回的;

    4、线程状态判定

    GetExitCodeThread

    BOOL GetExitCodeThread( HANDLE hThread, LPDWORD lpExitCode ); 参数名参数类型含义hThreadHANDLE需要获得退出状态的线程句柄lpExitCodeLPDWORD传的是地址,用来接收线程退出状态

    返回值

    非阻塞函数,调用后立即返回;如果指定的线程没有结束并且函数返回成功,状态返回 STILL_ACTIVE;这个函数只有当线程结束后才会返回合法的退出码,所以 STILL_ACTIVE 不能作为一个合法的退出码;

    5、销毁线程

    CloseHandle

    BOOL CloseHandle( HANDLE hObject ); 参数名参数类型含义hObjectHANDLE需要关闭的线程句柄

    二、线程类的封装

    1、设计思路

    1)提供一个线程类供继承,实现多态;2)线程类的析构函数定义成 virtual,避免内存泄漏;3)接口设计:线程启动、线程终止、线程存活判定、线程工作内容;4)线程工作内容是继承它的类需要做的事情,所以需要设计成虚函数;

    2、头文件设计

    Thread.h

    #include <windows.h> #include <process.h> class Thread { public: Thread(); virtual ~Thread(); bool Start(); void Stop(); bool IsRunning() const; unsigned int GetId(); protected: virtual void DoWork() {} private: static unsigned WINAPI ThreadProc(void* pvDerivedThread); void* m_pkHandle; string m_kName; unsigned int m_Id; };

    3、接口实现

    Thread.cpp

    #include "Thread.h" Thread::Thread(): m_pkHandle(0), m_Id(0) { } Thread::~Thread() { Stop(); } unsigned WINAPI Thread::ThreadProc(void* pvDerivedThread) { Thread* pThread = (Thread*)pvDerivedThread; if (pThread) { pThread->DoWork(); } return 0; } bool Thread::Start() { if (m_pkHandle || this->IsRunning()) { return false; } m_pkHandle = (HANDLE)_beginthreadex(NULL, 0, &ThreadProc, this, 0, &m_Id); return m_pkHandle != NULL; } void Thread::Stop() { if (!m_pkHandle) { return; } WaitForSingleObject((HANDLE)m_pkHandle, INFINITE); CloseHandle((HANDLE)m_pkHandle); m_pkHandle = NULL; } bool Thread::IsRunning() const { if (m_pkHandle) { DWORD exitCode = 0; if (GetExitCodeThread((HANDLE)m_pkHandle, &exitCode)) { if (STILL_ACTIVE == exitCode) { return true; } } } return false; } unsigned int Thread::GetId() { return m_Id; }

    4、接口分析

    1)线程类析构的时候调用 Stop,确保线程能够顺利结束;2)ThreadProc 作为类静态函数,方便实现线程回调函数的传参;3)Start() 为实际创建线程的函数,创建线程后,DoWork 就开始工作了;4)Stop() 为实际结束线程的函数,结束线程前需要调用 WaitForSingleObject 确保线程的回调函数 ThreadProc 已经返回了;5)IsRunning() 用来判断线程回调函数 ThreadProc 是否已经顺利返回,当返回 false 的时候,才能去调用 Stop();

    三、线程类的使用

    1、线程类继承

    实现一个线程类,要求输出倒计时3,2,1,然后自行结束线程; class LogicService : public Thread { public: LogicService() : m_bStop(false) { } void setIndentation(int ind) { m_iIndentation = ind; } protected: virtual void DoWork() { int iStopCount = 3; while (!m_bStop) { // 为了区别每条线程,用不同的缩进 for (int i = 0; i < m_iIndentation; ++i) { printf(" "); } printf("Thread(%d) %d Count down!\n", GetId(), iStopCount); Sleep(10); --iStopCount; if (iStopCount == 0) { m_bStop = true; } } printf("Thread(%d) exit!\n", GetId()); } private: bool m_bStop; int m_iIndentation; };

    2、线程类调用

    #define MAXT 4 int main() { LogicService *pLS = new LogicService[MAXT]; if (pLS) { for (int i = 0; i < MAXT; ++i) { pLS[i].setIndentation(i); pLS[i].Start(); } } Sleep(100); delete [] pLS; return 0; } 为了区别每条线程,用不同的缩进进行输出方便观看;输出如下: Thread(154472) 3 Count down! Thread(154256) 3 Count down! Thread(153952) 3 Count down! Thread(154252) 3 Count down! Thread(154472) 2 Count down! Thread(153952) 2 Count down! Thread(154256) 2 Count down! Thread(154252) 2 Count down! Thread(154472) 1 Count down! Thread(154256) 1 Count down! Thread(153952) 1 Count down! Thread(154252) 1 Count down! Thread(154472) exit! Thread(154252) exit! Thread(153952) exit! Thread(154256) exit!
    Processed: 0.037, SQL: 8