创建线程
这里用了createThread函数,但是最好不要用这个,这里为了和书上学习一致,使用这个函数
1、函数原型
HANDLE WINAPI CreateThread(
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_opt_ LPDWORD lpThreadId
);
2、参数意义
第一个参数 lpThreadAttributes 表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
第二个参数 dwStackSize 表示线程栈空间大小。传入0表示使用默认大小(1MB)。
第三个参数 lpStartAddress 表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。
第四个参数 lpParameter 是传给线程函数的参数。
第五个参数 dwCreationFlags 指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。
第六个参数 lpThreadId 将返回线程的ID号,传入NULL表示不需要返回该线程ID号。
简单demo
#include "pch.h"
#include <iostream>
#include <Windows.h>
DWORD WINAPI subthread(LPVOID);
int main()
{
HANDLE hThead;
DWORD theadId;
hThead=CreateThread(NULL, 0, subthread, 0, 0, &theadId);
printf("我是主线程, pid = %d,i have a sub thread pid=%d\n", GetCurrentThreadId(), theadId); //输出主线程pid
Sleep(2000);
}
DWORD WINAPI subthread(LPVOID p)
{
printf("我是子线程, pid = %d\n", GetCurrentThreadId()); //输出子线程pid
return 0;
}
等待线程返回
我们在学习工作中常常要用到等待线程返回,这里主要有两个函数WaitForSingleObject和WaitForMulitpleObjects
WaitForSingleObject(handle ,milli),参数一看就明白,不多说。
需要注意的一点就是如果想让某个线程一直等待就将第二个参数设置为INFINITE
WaitForMulitpleObjects
函数原型
DWORD WINAPI WaitForMultipleObjects(
_In_ DWORD nCount,
_In_ const HANDLE *lpHandles,
_In_ BOOL bWaitAll,
_In_ DWORD dwMilliseconds
);
第一个参数 DWORD dwCount 为等待的内核对象个数,可以是0到MAXIMUM_WAIT_OBJECTS(64)中的一个值。
第二个参数 CONST HANDLE* phObjects 为一个存放被等待的内核对象句柄的数组
第三个参数 BOOL bWaitAll 是否等到所有内核对象为已通知状态后才返回,如果为TRUE,则只有当等待的所有内核对象为已通知状态时函数才返回,如果为FALSE,则只要一个内核对象为已通知状态,则该函数返回。
第四个参数 DWORD dwMilliseconds 为等待时间,和WaitForSingleObject中的dwMilliseconds参数类似。
如果WaitForMultipleObjects()第三个参数为true表示等待所有线程返回,false表示有一个返回等待线程就继续运行
printf("我是主线程, pid = %d\n", GetCurrentThreadId()); //输出主线程pid
HANDLE handles[10] = {0};
for (int i = 0; i < 10; i++)
{
handles[i] = CreateThread(NULL, 0, subthread, &i, 0, &theadId);
}
//有一个线程结束就返回
WaitForMultipleObjects(10, handles, false, INFINITE);
}
DWORD WINAPI subthread(LPVOID p)
{
int n = *(int*)p;
Sleep(1000 * n); //第 n 个线程睡眠 n 秒
printf("我是子线程, pid = %d\n", GetCurrentThreadId()); //输出子线程pid
return 0;
}
注意上面我们的多个线程并不存在互斥访问资源的现象,但是当我们实际开发时,就会出现互斥访问,需要进行资源同步。
关键段 CriticalSection 声明及相关函数
来看下面的代码
#include "pch.h"
#include <iostream>
#include <Windows.h>
DWORD WINAPI subthread(LPVOID);
UINT count = 0;
int main()
{
HANDLE hThead;
DWORD theadId;
printf("我是主线程, pid = %d\n", GetCurrentThreadId()); //输出主线程pid
HANDLE handles[10] = {0};
for (int i = 0; i < 10; i++)
{
handles[i] = CreateThread(NULL, 0, subthread, &i, 0, &theadId);
}
WaitForMultipleObjects(10, handles, true, INFINITE);
printf("总共10个线程对count进行操作,结果为:count=%d", count);
}
DWORD WINAPI subthread(LPVOID p)
{
Sleep(50);
count++;
printf("我是子线程, pid = %d\n", GetCurrentThreadId()); //输出子线程pid
return 0;
}
这里会出现多多个线程同时访问的情况,在我机器上Count=8(每个机器不一样,每次跑都不确定)。现在使用CriticalSection解决这个问题
相关函数
1.函数功能:初始化,定义关键段变量后必须先初始化。
void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
2.函数功能:销毁,用完之后记得销毁。
void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
3.函数功能:进入关键区域,系统保证各线程互斥的进入关键区域。
void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
4.函数功能:离开关关键区域
void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
#include "pch.h"
#include <iostream>
#include <Windows.h>
DWORD WINAPI subthread(LPVOID);
UINT count = 0;
CRITICAL_SECTION cs;
int main()
{
HANDLE hThead;
DWORD theadId;
InitializeCriticalSection(&cs);//初始化关键段
printf("我是主线程, pid = %d\n", GetCurrentThreadId()); //输出主线程pid
HANDLE handles[10] = {0};
for (int i = 0; i < 10; i++)
{
handles[i] = CreateThread(NULL, 0, subthread, &i, 0, &theadId);
}
WaitForMultipleObjects(10, handles, true, INFINITE);
printf("总共10个线程对count进行操作,结果为:count=%d", count);
DeleteCriticalSection(&cs);
}
DWORD WINAPI subthread(LPVOID p)
{
Sleep(50);
EnterCriticalSection(&cs);
count++;
LeaveCriticalSection(&cs);
printf("我是子线程, pid = %d\n", GetCurrentThreadId()); //输出子线程pid
return 0;
}
和信号互斥量大同小异有木有。好啦再见啦,继续加油哟。
附上参考博客
链接: [博客园](https://www.cnblogs.com/ay-a/p/8810766.htmlt).