C++多线程编程:多线程同步之临界区 CriticalSection
文章目录
1. 头文件
#include <Windows.h>
2. CreateThread创建线程
CreateThread function (processthreadsapi.h) - Win32 apps | Microsoft Docs
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
__drv_aliasesMem LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
Create Thread是 Windows ap中在主线程的基础上创建一个新线程。创建成功之后会返回一个 h thread的 handle,且内核对象的计数加1,losehandle之后,引用计数减1,当变为0时,系统删陵内核对象。handle仅仅是线程的一个"标识"。
3. CloseHandle关闭线程句柄
CloseHandle function (handleapi.h) - Win32 apps | Microsoft Docs
BOOL CloseHandle(
HANDLE hObject
);
4. SuspendThread挂起指定的线程
SuspendThread function (processthreadsapi.h) - Win32 apps | Microsoft Docs
DWORD SuspendThread(
HANDLE hThread
);
5. ResumeThread恢复被挂起的线程的执行
ResumeThread function (processthreadsapi.h) - Win32 apps | Microsoft Docs
DWORD ResumeThread(
HANDLE hThread
);
6. Sleep休眠线程的执行
VOID WINAPI Sleep( __in DWORD dwMilliseconds);
dwMilliseconds:毫秒数
7. 示例代码1
#include<windows.h>//线程相关头文件
#include<iostream>
using namespace std;
//线程处理函数,windows标准调用约定_stdcall
DWORD WINAPI ThreadFun ( LPVOID lpThreadParameter );
int main()
{
DWORD threadId=0;
HANDLE hThread=CreateThread(NULL,//设为NULL使用默认安全性
0,//如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。
ThreadFun,//线程处理函数,函数名就是函数指针
"hello",//向线程函数传入的参数
0,//创建后马上运行
&threadId);
if (hThread == NULL)
{
cout << "线程创建失败:" << GetLastError() << endl;
}
cout << "线程的句柄:" << hThread << endl;
cout << "子线程的ID:" << threadId << endl;
cout << "主线程ID:" << GetCurrentThreadId() << endl;
//关闭线程句柄,引用计数-1, 并没有结束线程
//CloseHandle(hThread);//表示以后不再引用句柄
getchar();
//挂起线程
SuspendThread(hThread);
getchar();
//恢复线程执行
ResumeThread(hThread);
getchar();
return 0;
}
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter)
{
char *str = (char*)lpThreadParameter;
while(TRUE)
{
cout << "线程处理函数中:"<<str << endl;
cout << "子线程ID: " << GetCurrentThreadId() << endl;
Sleep(1000);//休眠1秒
}
return 0;
}
8.WaitForSingleobject
等待一个内核对象变为已通知状态
WaitForSingleObject function (synchapi.h) - Win32 apps | Microsoft Docs
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
该函数需要传递一个内核对象句柄,如果该内核对象处于未通知状态,则该函数导致线程进阻塞状态;如果该内核对象处于已通知状态则该函数立即返回 WAIT_OBJECT_0
。第二个参数指明要等待的时间(毫秒),INFINITE表示无限等待,如果第二个参数为0
,那么函数立即返回。如果等待超时,该函数返WAIT_TIMEOUT
。
如果该函数失败,返回 WAIT_FAILED
。
9. 终止线程
终止线程运行的最佳方法
是让它的线程函数 return返回
。
除此之外,终止线程有两个函数
ExitThread()
: 避免使用
TerminateThread()
:必须避免
9.1 ExitThread()
ExitThread function (processthreadsapi.h) - Win32 apps | Microsoft Docs
void ExitThread(
DWORD dwExitCode
);
可以让线程调用 ExitThread函数,以便强制线程终止运行:如果使用这两种方法退出线程,则不会执行线程函数的 return语句,所以就不会调用线程函数作用域内申请的类对象的析构函数,会造成內存泄露。
9.2 TerminateThread()
TerminateThread function (processthreadsapi.h) - Win32 apps | Microsoft Docs
BOOL TerminateThread(
HANDLE hThread,
DWORD dwExitCode
);
Terminate thread能够撤消任何线程,hThread参数用于标识被终止运行的线程的句柄。dwExitcode参数就是线程终止的退出码。同时,线程的内核对象的使用计数也被递减。注意 Terminate thread函数是异步运行的函数当函数返回时,并不能保证线程被撤消。如果需要确切地知道该线程已经终止运行,使用 WaitForSingleobject或者类似的函数,
windows核心编程: 一个设计良好的应用程序决不会使用这个函数,因为被终止运行的线程收不到它被“杀死”的通知,导致线程不能正确地清除。
9.3 GetExitCodeThread获取线程结束码
GetExitCodeThread function (processthreadsapi.h) - Win32 apps | Microsoft Docs
BOOL GetExitCodeThread(
HANDLE hThread,
LPDWORD lpExitCode
);
10. 示例代码2
情况1:
#include<windows.h>
#include<stdio.h>
DWORD WINAPI ThreadFun( LPVOID lpThreadParameter );
int main()
{
printf("主线程开头!\n");
//创建线程
HANDLE hThread = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);
//无限等待,除非子线程运行结束
WaitForSingleObject(hThread, INFINITE);
printf("主线程结尾!\n");
return 0;
}
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter)
{
int n = 0;
while (++n<=6)
{
printf("第%d次打印hello!\n", n );
Sleep(1000);
if (n == 3)
{
//退出线程,退出码为666,避免使用
ExitThread(666);
}
}
return 0;
}
情况2:
#include<windows.h>
#include<stdio.h>
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter);
int main()
{
printf("主线程开头!\n");
//创建线程
HANDLE hThread = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);
DWORD code;
GetExitCodeThread(hThread, &code);
if (code == STILL_ACTIVE)//如果线程没有结束,返回STILL_ACTIVE
{
printf("子线程没有退出!\n");
}
//等待3秒,超时会返回
DWORD ret=WaitForSingleObject(hThread, 3000);
if (ret == WAIT_TIMEOUT)
{
printf("WaitForSingleObject等待子线程退出超时!\n");
}
//强制终止线程、避免使用
TerminateThread(hThread, 888);
//无限等待,除非子线程运行结束
WaitForSingleObject(hThread, INFINITE);
GetExitCodeThread(hThread, &code);
printf("子线程退出码%d!\n",code);
printf("主线程结尾!\n");
return 0;
}
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter)
{
int n = 0;
while (++n <= 6)
{
printf("第%d次打印hello!\n", n);
Sleep(1000);
}
return 0;
}
情况3:
#include<windows.h>
#include<stdio.h>
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter);
int main()
{
printf("主线程开始!\n");
//创建线程
HANDLE hThread1 = CreateThread(NULL, 0, ThreadFun,(LPVOID) "A", 0, NULL);
HANDLE hThread2 = CreateThread(NULL, 0, ThreadFun, (LPVOID) "B", 0, NULL);
HANDLE hThread3 = CreateThread(NULL, 0, ThreadFun, (LPVOID) "C", 0, NULL);
HANDLE handleArr[] = { hThread1 ,hThread2 ,hThread3};
//无限等待所有线程结束
//DWORD ret = WaitForMultipleObjects(3,handleArr,true,INFINITE) ;
// printf("ret== %d\n" ,ret== WAIT_OBJECT_0);
//等待任意一个线程结束就返回数组索引
//DWORD ret = WaitForMultipleObjects(3, handleArr, false, INFINITE);
//printf("ret== %d\n", ret);
//设置等待超时时间1秒
DWORD ret = WaitForMultipleObjects(3, handleArr, false, 1000);
printf("ret== %d\n", ret== WAIT_TIMEOUT);
printf("主线程结尾!\n");
return 0;
}
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter)
{
char * name = (char *)lpThreadParameter;
if (strcmp(name, "A") == 0)
{
Sleep(10000);
}
else if (strcmp(name, "C") == 0)
{
Sleep(3000);
}
else
{
Sleep(8000);
}
printf("%s线程结束!\n", name);
return 0;
}
11. _beginthread
《windows核心编程》
CreateThread函数是用来创建线程的Windows函数。不过,如果你正在编写C/C++代码,决不应该调用 CreateThread
。相反,应该使用 Visual c++运行期库函数_beginthread
在 Create thread ap创建的线程中使用sprintf,malloc,strcat等涉及CRT存储堆操作的CRT库函数是很危险的,容易造成线程的意外中止。而使用 _beginthread
和 _beginthreadex
创建的线程中可以安全的使用CRT函数,但是必须在线程结束的时候相应的调用 _endthread
或_endthreadex
。
如果你在线程函数中进行以下操作,你就应该使用 _beginthread
和 _endthread
:
(1)使用 malloc()
和free()
,或是new
和 delete
(2)使用 stdio.h
或io.h
里面声明的任何函数
(3)使用浮点变量或浮点运算函数
(4)调用任何一个使用了静态缓冲区的 runtime
函数比如 asctime()
,strtok()
或 rand()
11.1 头文件
#include <process.h>
11.2 _beginthread创建线程
_beginthread、_beginthreadex | Microsoft Docs
uintptr_t _beginthread( // NATIVE CODE
void( __cdecl *start_address )( void * ),
unsigned stack_size,
void *arglist
);
uintptr_t _beginthread( // MANAGED CODE
void( __clrcall *start_address )( void * ),
unsigned stack_size,
void *arglist
);
uintptr_t _beginthreadex( // NATIVE CODE
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
uintptr_t _beginthreadex( // MANAGED CODE
void *security,
unsigned stack_size,
unsigned ( __clrcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
11.3 _endthread终止线程
_endthread、_endthreadex | Microsoft Docs
void _endthread( void );
void _endthreadex(
unsigned retval
);
_endthread
:终止由 _beginthread
创建的线程。对于使用 Libcmt.lib
链接的可执行文件,不要调用Win32
API的 Exitthread()
,它无法释放已分配的资源。而 _endthread
回收线程资源然后调用 ExitThread
。_endthread
自动关闭线程句柄。
12 示例代码3
#include <process.h>
#include <stdio.h>
#include<windows.h>
void ThreadFun(void* param);
int main()
{
printf("主线程开始!\n");
//推荐使用C++运行期库函数_beginthread
HANDLE hThread= (HANDLE) _beginthread(ThreadFun, 0, "hello");
WaitForSingleObject(hThread, INFINITE);
printf("主线程结束!\n");
}
void ThreadFun(void* param)
{
char *p = (char *)param;
int n = 0;
while (++n <= 6)
{
Sleep(1000);
printf("第%d次打印%s\n" , n, p );
//结束_beginthread创建的线程,别用ExitThread退出。
if (n == 3) _endthread();
}
printf("子线程结束!\n");
}