文章目录
一、线程 API 介绍
1、创建线程
_beginthreadex
unsigned long _beginthreadex(
void *security,
unsigned stack_size,
unsigned(_stdcall *start_address)(void *),
void *argilist,
unsigned initflag,
unsigned *threaddr
);
参数名 | 参数类型 | 含义 |
---|---|---|
security | void指针 | 安全属性, 为NULL时表示默认安全性 |
stack_size | 无符号整型 | 线程的堆栈大小, 一般默认为0 |
start_address | 函数地址 | 线程回调函数,参数类型为 void*,传 NULL 会有异常抛出 |
argilist | void指针 | 线程回调函数的参数,需要多个参数的时候传递结构体指针即可 |
initflag | 无符号整型 | 新线程的初始状态,0表示立即执行,CREATE_SUSPENDED 表示创建之后挂起,调用 ResumeThread 恢复 |
threaddr | 线程ID地址 | 用来接收线程ID,传 NULL 表示不使用 |
返回值
- 如果线程创建成功,返回一个该线程的句柄;如果创建失败,返回 0,并且置错误码 errno,错误码相关的可以参考:errno 简介;
2、恢复线程
ResumeThread
DWORD ResumeThread(
HANDLE hThread
);
参数名 | 参数类型 | 含义 |
---|---|---|
hThread | HANDLE | 需要恢复的线程句柄 |
- 如果在创建线程的时候,initflag 置上了标志位 CREATE_SUSPENDED,则代表线程创建后不会立即执行,需要调用 ResumeThread 进行线程恢复;
返回值
- 如果函数执行成功,则返回线程被挂起的次数;
返回值 | 原因 |
---|---|
0 | 线程没有被挂起 |
1 | 线程曾经被挂起,但是现在已经恢复 |
>1 | 线程还是处于挂起状态 |
- 如果函数执行失败,则返回 (DWORD) -1,具体错误码,通过 GetLastError 函数获取;
3、等待信号量
WaitForSingleObject
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
参数名 | 参数类型 | 含义 |
---|---|---|
hHandle | HANDLE | 等待信号的句柄,不仅仅是线程句柄,还可以是 信号量、进程、事件、控制台输入 等其它对象的句柄等等 |
dwMilliseconds | DWORD | 超时时间,如果这个参数是 INFINITE ,则忽略超时时间 |
返回值
- 这是一个阻塞函数,如果线程还在执行,这里是不会返回的;
4、线程状态判定
GetExitCodeThread
BOOL GetExitCodeThread(
HANDLE hThread,
LPDWORD lpExitCode
);
参数名 | 参数类型 | 含义 |
---|---|---|
hThread | HANDLE | 需要获得退出状态的线程句柄 |
lpExitCode | LPDWORD | 传的是地址,用来接收线程退出状态 |
返回值
- 非阻塞函数,调用后立即返回;
- 如果指定的线程没有结束并且函数返回成功,状态返回 STILL_ACTIVE;这个函数只有当线程结束后才会返回合法的退出码,所以 STILL_ACTIVE 不能作为一个合法的退出码;
5、销毁线程
CloseHandle
BOOL CloseHandle(
HANDLE hObject
);
参数名 | 参数类型 | 含义 |
---|---|---|
hObject | HANDLE | 需要关闭的线程句柄 |
二、线程类的封装
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!