简述
Window多线程开发是一种常见的解决程序并行的手段,开启线程本身非常简单,只需调用一个系统API就可以了。然而这样的线程是无序的,混乱的。因此在实际开发中必须对线程的行为进行管控,这就是线程的同步问题了,也是多线程开发中的重点。
一:一个简单的多线程程序
源码:
#include <stdio.h>
#include <process.h>
#include <windows.h>
//子线程函数
unsigned int __stdcall ChildThreadFunc1(PVOID pM)
{
while (true)
{
Sleep(500);
printf("子线程1在运行\n");
}
return 0;
}
//子线程函数
DWORD WINAPI ChildThreadFunc2(LPVOID pM)
{
while (true)
{
Sleep(500);
printf("子线程2在运行\n");
}
return 0;
}
int main()
{
HANDLE handle1;
HANDLE handle2;
handle1 = (HANDLE)_beginthreadex(NULL, 0, ChildThreadFunc1, NULL, 0, NULL);
handle2 = (HANDLE)CreateThread(NULL, 0, ChildThreadFunc2, NULL, 0, NULL);
while (true)
{
Sleep(500);
printf("+++++++++主线程在运行\n");
}
WaitForSingleObject(handle1, -1);
WaitForSingleObject(handle2, -1);
getchar();
return 0;
}
效果:
在主线程之外我们又开了两个线程,这三个线程都会进入到一个while循环,各自执行自己的代码段。显然,并行的效果达到了。
解读
(1)相关API详解
函数原型:
CreateThread(
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_ __drv_aliasesMem LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_opt_ LPDWORD lpThreadId
);
功能:创建一个线程,并返回线程句柄
参数:
lpThreadAttributes:一般传入NULL表示使用默认设置。
dwStackSize: 示线程栈空间大小。传入0表示使用默认大小(1MB)。
lpStartAddress: 线程所执行的线程函数地址,多个线程可以使用同一个函数地址。
lpParameter: 传给线程函数的参数。
dwCreationFlags: 为0表示线程创建之后立即可运行,如果为CREATE_SUSPENDED则表示 线程创建后挂起, 直到调用ResumeThread()激活后才运行。
lpThreadId: 传出参数,返回线程的ID号。
返回值:成功则返回新线程的句柄,失败返回NULL。
函数原型:
WINBASEAPI DWORD WINAPI WaitForSingleObject(_In_ HANDLE hHandle,_In_ DWORD dwMilliseconds);
功能:阻塞等待一个线程结束
参数:
hHandle:线程句柄
dwMilliseconds:阻塞时间,如果为零则函数立即返回,如果为负,则函数永久阻塞,直到该线程结束
(2)程序解读
程序刚进来的时候声明了两个线程句柄
HANDLE handle1;
HANDLE handle2;
紧接着就创建了两个线程,这儿用了_beginthreadex()和CreateThread()两种方式创建,他们的使用方法相同。_beginthreadex()在CreateThread()之上又封装了一层,最终还是会调用到CreateThread(),但是使用beginthreadex()创建的线程更加安全(详情参见:https://www.cnblogs.com/project/archive/2011/08/21/2147634.html)。推荐使用_beginthreadex。
然后调用了WaitForSingleObject使主线程阻塞,直到所有的子线程运行结束。如果子线程正在运行,主线程就退出了,那么子线程也会被强制退出。