1. 线程的创建
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程安全属性
SIZE_T dwStackSize, //线程堆栈大小
LPTHREAD_START_ROUTINE lpStartAddress, //重要: 线程函数指针
LPVOID lpParameter, //重要: 启动线程函数
DWORD dwCreationFlags, //线程安全属性
LPDWORD lpThreadId //返回 TID
);//返回新建线程的句柄
1.1 线程函数
要符合接口参数要求
//ThreadProc 函数原型
DWORD WINAPI ThreadProc(
LPVOID lpParameter
);
1.2 线程句柄与 TID
每一个线程都有一个句柄和一个标识符 (TID)。
TID 是 DWORD 类型,每个线程的 TID 都不同,所以可以用 TID 标识唯一的 线程。
- 通过 TID 和 OpenThread() 可以获取线程的句柄。
- 通过 句柄 和 GetThreadId()可以获取线程的TID。
- GetCurrentThread() 获取本线程句柄。
- GetCurrentThreadId() 获取本线程 TID。
1.3 创建线程代码
#include <windows.h>
#include <cstdio>
#define MAX_THREADS 5
typedef struct _THREAD_PARAM{
DWORD i;
DWORD dwRandom;
DWORD dwData;
} THREAD_PARAM, *LPTHREAD_PARAM;
/****************
功能:线程函数,将参数打印出来
****************/
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
//参数数据类型
LPTHREAD_PARAM pData;
pData = (LPTHREAD_PARAM)lpParam;
printf("TID = %u,\tparameters = %u, %u, %u\n",
GetCurrentThreadId(),
pData->i,
pData->dwRandom,
pData->dwData);
//释放保存参数的内存(主线程中分配的)
HeapFree(GetProcessHeap(), 0, pData);
return 0;
}
/*********************
功能:创建多个线程
******************/
int main(int argc, char **argv)
{
LPTHREAD_PARAM pData;
DWORD dwThreadId[MAX_THREADS];
HANDLE hThread[MAX_THREADS];
int i;
//创建 MAX_THREADS 个线程
for(i = 0 ;i < MAX_THREADS; ++i){
//为线程函数参数分配内存
pData = (LPTHREAD_PARAM)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, sizeof(THREAD_PARAM));
if(pData == NULL){
printf("HeapAlloc error;\n");
ExitProcess(2);
}
//设置参数
pData->i = i;
pData->dwRandom = rand();
pData->dwData = 100;
//创建线程
hThread[i] = CreateThread(
NULL, //默认安全属性
0, //默认堆栈大小
ThreadProc, //线程函数
pData, //参数
0, //默认创建标志
&dwThreadId[i]);//返回TID
if(hThread[i] == NULL){
printf("hThread[%d]创建失败!\n",i);
ExitProcess(i);
}
Sleep(10);
}
//主线程等待其他子线程执行结束
WaitForMultipleObjects(MAX_THREADS, hThread, TRUE, INFINITE);
//关闭所有线程的句柄
for(i = 0; i < MAX_THREADS; ++i){
CloseHandle(hThread[i]);
}
return 0;
}
代码运行结果:
2. 线程的挂起、恢复、切换、终止
//schedule.cpp
#include <windows.h>
#include <cstdio>
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
LPDWORD pData = (LPDWORD)lpParam;
DWORD i = 0;
for(i = 0; i < 10; ++i){
Sleep(100);//每 ms 打印一次
printf("TID = %u,\tparameters = %u\ti = %u\n",
GetCurrentThreadId(), *pData, i);
}
ExitThread(i);
return 0;
}
/*********************
功能:线程调度
*********************/
int main(int argc, char **argv)
{
DWORD dwData;
DWORD dwThreadId[2];
HANDLE hThread[2];
//创建线程
dwData = 1;
hThread[0] = CreateThread(
NULL,0,
ThreadProc,
&dwData,
CREATE_SUSPENDED, //挂起新建的进程
&dwThreadId[0]
);
if(hThread[0] == NULL){
ExitProcess(0);
}
//创建线程
dwData = 2;
hThread[1] = CreateThread(NULL,
0,
ThreadProc,
&dwData,
0,
&dwThreadId[1]);
if(hThread[1] == NULL){
ExitProcess(1);
}
//等待 200ms 恢复线程的执行
Sleep(200);
ResumeThread(hThread[0]);
//挂起线程的执行
SuspendThread(hThread[1]);
//等待 300 ms 终止线程,恢复线程
Sleep(300);
TerminateThread(hThread[0], 0);
ResumeThread(hThread[1]);
//等待所有线程执行结束
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
//关闭所有线程的句柄
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
return 0;
}
运行结果:
3. 等待函数
等待函数可以等待同步对象以外,还可以等待其他对象,包括进程和线程。以进程或线程句柄为等待对象就可以了。
以下两个是下面的同步代码中用到的。
在下面代码中用到的时候添加了注释。
3.1 等待函数
//WaitForSingleObject
//功能是等待单个对象,如果对象被标记,则返回
DWORD WINAPI WaitForSingleObject(
DANDLE hHandle,
DWORD dwMilliseconds
);
//WaitForMultipleObjects
// 功能是等待多个对象,等待的所有对象都设置为标记的 / 有一个设置为标记的 则返回.
DWORD WINAPI WaitForMultipleObjects(
DWORD nCount,
const HANDLE *lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
);
3.2 标记函数
设置标记:
//设置标记
//将事件对象设置为标记
BOOL WINAPI SetEvent(HANDLE hEvent);
重置标记:
//重置标记
//如果事件设置为手工重置,那么需要使用此函数来重置事件
BOOL WINAPI ResetEvent(HANDLE hEvent);
获取事件句柄:
//从事件名中获取事件句柄
HANDLE WINAPI OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
4. 多个线程互同步
创建三个线程,对一个全局变量进行读操作。
通过三个读事件和写事件控制三个线程,避免在写入数据的时候发生【读事件】。
- 主线程创建3个线程,创建【读事件】和【写事件】,并设置为false.
- 主线程开始写入数据,之后打开【写事件】,并开始等待【读事件】。
- 子线程之前的【写事件】状态是关闭,所以在等待。【写事件】打开后,开始执行打印数据操作。然后打开【读事件】。
- 最后关闭子进程。
#include <windows.h>
#include <cstdio>
//常量定义
#define NUMTHREADS 3
#define BUFFER_SIZE 16
#define FOR_TIMES 5
//全局变量
HANDLE hThread[NUMTHREADS];
//写 event 表示写操作是否完成
HANDLE hWriteEvent[NUMTHREADS];
//读 event 表示读操作是否完成
HANDLE hReadEvents[NUMTHREADS];
//共享内存
// BYTE lpSharedBuffer[16] = {0};
LPSTR lpSharedBuffer;
DWORD* index;
//函数声明
void MultiEvents(void);
void WriteToBuffer(void);
DWORD WINAPI ThreadFunction(LPVOID lpParam);
/******************
int main(void)
******************/
int main(void)
{
index = (DWORD*)malloc(sizeof(DWORD));
lpSharedBuffer = (char*)malloc(sizeof(char)*64);
MultiEvents();
free(index);
free(lpSharedBuffer);
return 0;
}
/*******************
演示 event 的使用方法
******************/
void MultiEvents(void)
{
// HANDLE hThread;
DWORD i = 0;
*index = i;
//创建多个线程,读共享内存,主线程写共享内存
//每个线程都有对应的读写同步事件
for(i = 0; i < NUMTHREADS; ++i){
*index = i;
//每个线程都有一个 event 表示写入操作完成
hWriteEvent[i] = CreateEvent(
NULL, //默认安全属性
FALSE, //自动重置
FALSE, //初始化为未置位的
NULL //未命名
);
//判断是否创建成功
if(hWriteEvent[i] == NULL){
printf("CreateEvent failed (%d)\n",GetLastError());
return ;
}
//每个线程都有一个 event 表示读入操作完成
hReadEvents[i] = CreateEvent(
NULL, //默认安全属性
FALSE, //自动重置
FALSE, //初始化为未置位的
NULL //未命名
);
//判断是否创建成功
if(hReadEvents[i] == NULL){
printf("CreateEvent failed (%d)\n",GetLastError());
return ;
}
//创建线程
hThread[i] = CreateThread(NULL, 0,
ThreadFunction,
index,
0, NULL
);
if(hThread[i] == NULL){
printf("CreateThread failed (%d)\n",GetLastError());
return ;
}
printf("CreateThread: %d\n",i);
}
WriteToBuffer();
//主线程等待其他子线程执行结束
WaitForMultipleObjects(NUMTHREADS,
hThread, TRUE, INFINITE);
//关闭所有线程的句柄
for(i = 0; i < NUMTHREADS; ++i){
CloseHandle(hThread[i]);
printf("CloseThread:%d\n",i);
}
return ;
}
/************************
由主线程调用,向共享内存中写入数据,
等待所有读线程读完后函数返回
*********************/
void WriteToBuffer(void)
{
DWORD dwWaitResult, j, i;
//完成 for_times 次读写
for(j = 0;j < FOR_TIMES; ++j){
//写入需要的时间间隔
Sleep(rand()%100);
//写入共享内存
wsprintf(lpSharedBuffer,"shared %d",j);
//将线程对应的写 Event 置为 "标志的",表示写操作完成
//其他线程可以开始读
for(i = 0; i < NUMTHREADS; ++i){
if(! SetEvent(hWriteEvent[i])){
printf("SetEvent failed (%d)\n",GetLastError());
return ;
}
}
dwWaitResult = WaitForMultipleObjects(
NUMTHREADS, //Event 句柄的个数
hReadEvents, //Event句柄数组
TRUE, //等到所有的event都被标志
INFINITE //无线等待
);
//判断等待结果
if(dwWaitResult != WAIT_OBJECT_0){
printf("Wait errpr (%d)\n",GetLastError());
ExitProcess(0);
}
}
return ;
}
/*******************
线程函数,读共享内存
*******************/
DWORD WINAPI ThreadFunction(LPVOID lpParam)
{
DWORD dwWaitResult;
LPSTR lpRead[16];
DWORD j = 0;
DWORD dwThreadIndex = *(DWORD*)lpParam;
printf("Hello! dwThreadIndex = %d\n",dwThreadIndex);
//完成 FOR_TIMES 次读写
for(; j < FOR_TIMES; ++j)
{
//等待写事件置位,表示数据已经写入
dwWaitResult = WaitForSingleObject(
hWriteEvent[dwThreadIndex], //event 句柄
INFINITE //无限等待
);
switch(dwWaitResult)
{
case WAIT_OBJECT_0:
//模拟数据处理需要的时间间隔
Sleep(rand()%10);
CopyMemory(lpRead, lpSharedBuffer, 16);
break;
//发生错误
default:
printf("wait errpr: %d\n",GetLastError());
ExitProcess(0);
}
//将读 event 置位,表示读操作完成
if(!SetEvent(hReadEvents[dwThreadIndex])){
printf("SetEvent failed (%d)\n",GetLastError());
return 0;
}
//打印读到的内容
printf("线程 %u\t第 %d 次读, 内容: %s\n",
dwThreadIndex,j,(LPSTR)lpRead);
}
return 1;
}
运行结果:
5. C++11 中的互斥
#include <iostream>
#include <thread>
#include <mutex>
#include <windows.h>
std::mutex mtx; //全局互斥锁
int shared_resource = 0;
void Num1(void){
for(int i = 0; i < 3; ++i){
mtx.lock();//锁定互斥锁
++shared_resource;
printf("This is AddNum!\n");
mtx.unlock();//解锁互斥锁
}
return ;
}
void Num2(void){
for(int i = 0; i < 3; ++i){
mtx.lock();
++shared_resource;
printf("This is PrintNum!\n");
mtx.unlock();
}
return ;
}
int main(void){
std::thread t1(Num1);
std::thread t2(Num2);
t1.join();
t2.join();
std::cout <<"Final value of shared_resource: " << shared_resource << std::endl;
return 0;
}
运行结果: