每个线程都具有自己的内存栈
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpsa,//安全属性(在windows开发中安全属性都是废弃的参数)
DWORD cbStack, //线程栈的大小(按照1M对齐)
LPTHREAD_START_ROUTINE lpStartAddr,//线程处理函数的函数地址
LPVOID lpvThreadParam, //传递给线程处理函数的参数
DWORD fdwCreate,//线程的创建方式(立即执行方式,挂起方式)
LPDWORD lpIDThread//创建成功,返回线程的ID
); //创建成功,返回线程句柄
DWORD ThreadProc(
LPVOID lpParameter//创建线程时,传递给线程的参数(创建线程时填入的lpvThreadParam)
);
示例代码:
#include
#include
DWORD CALLBACK TestProc(LPVOID pParam){
char* pszText = (char*)pParam;
while(1){
printf("%s\n", pszText);
Sleep(1000);
}
return 0;
}
DWORD CALLBACK TestProc2(LPVOID pParam){
char* pszText = (char*)pParam;
while(1){
printf("%s\n", pszText);
Sleep(1000);
}
return 0;
}
int main(){
DWORD nID = 0;
char* pszText = "************";
HANDLE hThread = CreateThread(NULL,0,TestProc,pszText,0,&nID);
char* pszText2 = "-----------";
HANDLE hThread2 = CreateThread(NULL,0,TestProc2,pszText2,0,&nID);
getchar();//让主线程不结束,能让子线程执行
return 0;
}
DWORD SuspendThread(
HANDLE hThread//handle of thread
);
DWORD ResumeThread(
HANDLE hThread//handle of thread
);
示例代码:按回车键切换两个线程的状态
#include
#include
DWORD CALLBACK TestProc(LPVOID pParam){
char* pszText = (char*)pParam;
while(1){
printf("%s\n", pszText);
Sleep(1000);
}
return 0;
}
DWORD CALLBACK TestProc2(LPVOID pParam){
char* pszText = (char*)pParam;
while(1){
printf("%s\n", pszText);
Sleep(1000);
}
return 0;
}
int main(){
DWORD nID = 0;
char* pszText = "************";
HANDLE hThread = CreateThread(NULL,0,TestProc,pszText,0,&nID);
char* pszText2 = "-----------";
HANDLE hThread2 = CreateThread(NULL,0,TestProc2,pszText2,CREATE_SUSPENDED,&nID);//CREATE_SUSPENDED让线程休眠
getchar();//按回车键切换线程状态
SuspendThread(hThread);
ResumeThread(hThread2);
getchar();//让主线程不结束,能让子线程执行
return 0;
}
//结束指定线程(相当于他杀)
BOOL TerminateThread(
HANDLE hThread,//handle of thread
DWORD dwExitCode//exit code
);
//结束函数所在的线程(相当于线程自杀)
VOID ExitThread(
DWORD dwExitCode//exit code for this thread(这个参数没有实际的意义)
);
DWORD GetCurrentThreadId(void);
HANDLE GetCurrentThread(void);
DWORD WaitForSingleObject(
HANDLE hHandle,//句柄BUFF的地址
DWORD dwMilliseconds//最大等候时间,等待时间INFINITE无限
);
DWORD WaitForMultipleObjects(
DWORD nCount, //句柄数量
CONST HANDLE* lpHandles,//句柄BUFF的地址
BOOL bWaitAll,//等待方式
DWORD dwMilliseconds//等待时间,INFINITE表示无限时长
);
//bWaitAll:等待方式
//TRUE:表示所有句柄都有信号,才结束等待
//表示句柄中只要有1个有信号,就结束等待
示例代码:
#include
#include
DWORD CALLBACK TestProc(LPVOID pParam){
char* pszText = (char*)pParam;
while(1){
printf("%s\n", pszText);
Sleep(1000);
}
return 0;
}
DWORD CALLBACK TestProc2(LPVOID pParam){
char* pszText = (char*)pParam;
while(1){
printf("%s\n", pszText);
Sleep(1000);
}
return 0;
}
int main(){
DWORD nID = 0;
char* pszText = "************";
HANDLE hThread = CreateThread(NULL,0,TestProc,pszText,0,&nID);
//因为线程1执行的函数内部是while死循环所以线程句柄不会产生信号,
//而等待时间无限,导致程序在这里阻塞,线程2不可能创建
WaitForSingleObject(hThread, INFINITE);
char* pszText2 = "-----------";
HANDLE hThread2 = CreateThread(NULL,0,TestProc2,pszText2,0,&nID);
getchar();//按回车键切换线程状态
SuspendThread(hThread);
ResumeThread(hThread2);
getchar();//让主线程不结束,能让子线程执行
return 0;
}
++
运算时#include
#include
long g_value = 0;
DWORD CALLBACK TestProc1(LPVOID pParam){
for(int i=0; i<10000; i++){
g_value++;
}
return 0;
}
DWORD CALLBACK TestProc2(LPVOID pParam){
for(int i=0; i<10000; i++){
g_value++;
}
return 0;
}
int main(){
DWORD nID = 0;
HANDLE hThread[2];
hThread[0] = CreateThread(NULL, 0, TestProc1, NULL, 0, &nID);
hThread[1] = CreateThread(NULL, 0, TestProc2, NULL, 0, &nID);
WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
printf("%d\n",g_value);
return 0;
}
线程A
执行g_value++
时,如果线程切换时间正好是线程A将只保存到g_value
之前,线程B
继续执行g_value++
,那么当线程A
再次被切换回来之后,会将原来线程A
保存的值保存到g_value
上,线程V进行的加法操作将会被覆盖LONG InterlockedIncrement(
IN PLONG Addend
);
LONG InterlockedDecrement(
IN PLONG Addend
);
LONG InterlockedCompareExchange(
IN OUT PLONG Destination,
IN LONG Exchange,
IN LONG Comparand
);
LONG InterlockedExchange(
IN OUT PLONG Target,
IN LONG Value
);
//...还有大量的这样的原子锁函数
//原子锁的实现:直接对数据所在的内存操作,并且在任何一个瞬间只能有一个线程访问
示例代码:
#include
#include
long g_value = 0;
DWORD CALLBACK TestProc1(LPVOID pParam){
for(int i=0; i<10000; i++){
//g_value++;
InterlockedIncrement(&g_value);
}
return 0;
}
DWORD CALLBACK TestProc2(LPVOID pParam){
for(int i=0; i<10000; i++){
//g_value++;
InterlockedIncrement(&g_value);
}
return 0;
}
int main(){
DWORD nID = 0;
HANDLE hThread[2];
hThread[0] = CreateThread(NULL, 0, TestProc1, NULL, 0, &nID);
hThread[1] = CreateThread(NULL, 0, TestProc2, NULL, 0, &nID);
WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
printf("%d\n",g_value);
return 0;
}
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, //安全属性
BOOL bInitialOwner,//初始的拥有者 TRUE/FALSE
LPCTSTR lpName//命名
);//创建成功返回互斥句柄(互斥句柄是一个可等待的句柄)
//互斥的两个特点:
//在同一个进程中当某个时间点上只能有一个线程拥有互斥,所以互斥具有独占性和排他性
//当所有线程都不用有互斥时,互斥句柄有信号,当有任何一个线程拥有互斥时,互斥句柄无信号
WaitFor...
(WaitForSingleObject, WaitForMultipleObjects)互斥的等待遵循谁先等待谁先获取BOOL ReleaseMutex(
HANDLE hMutex//handle of mutex
);
BOOL CloseHandle(
HANDLE hObject
);
示例代码:解决打印内容乱套现象
#include
#include
DWORD CALLBACK TestProc(LPVOID pParam){
char* pszText = (char*)pParam;
while(1){
//printf("%s\n", pszText);
//Sleep(1000);
for(int i=0; i<strlen(pszText); i++){
printf("%c", pszText[i]);
Sleep(125);
}
printf("\n");
}
return 0;
}
DWORD CALLBACK TestProc2(LPVOID pParam){
char* pszText = (char*)pParam;
while(1){
//printf("%s\n", pszText);
//Sleep(1000);
for(int i=0; i<strlen(pszText); i++){
printf("%c", pszText[i]);
Sleep(125);
}
printf("\n");
}
return 0;
}
int main(){
DWORD nID = 0;
char* pszText = "********";
HANDLE hThread = CreateThread(NULL,0,TestProc,pszText,0,&nID);
char* pszText2 = "________";
HANDLE hThread2 = CreateThread(NULL,0,TestProc2,pszText2,0,&nID);
getchar();//让主线程不结束,能让子线程执行
return 0;
}
#include
#include
HANDLE g_hMutex = 0;//接收互斥句柄
DWORD CALLBACK TestProc(LPVOID pParam){
char* pszText = (char*)pParam;
while(1){
//printf("%s\n", pszText);
//Sleep(1000);
//等待获取到互斥锁
WaitForSingleObject(g_hMutex,INFINITE);
for(int i=0; i<strlen(pszText); i++){
printf("%c", pszText[i]);
Sleep(125);
}
printf("\n");
//释放互斥锁
ReleaseMutex(g_hMutex);
}
return 0;
}
DWORD CALLBACK TestProc2(LPVOID pParam){
char* pszText = (char*)pParam;
while(1){
//printf("%s\n", pszText);
//Sleep(1000);
//等待获取到互斥锁
WaitForSingleObject(g_hMutex,INFINITE);
for(int i=0; i<strlen(pszText); i++){
printf("%c", pszText[i]);
Sleep(125);
}
printf("\n");
//释放互斥锁
ReleaseMutex(g_hMutex);
}
return 0;
}
int main(){
g_hMutex = CreateMutex(NULL, FALSE, NULL);//FALSE表示创建出来的互斥任何线程都不用有它
DWORD nID = 0;
char* pszText = "********";
HANDLE hThread = CreateThread(NULL,0,TestProc,pszText,0,&nID);
char* pszText2 = "________";
HANDLE hThread2 = CreateThread(NULL,0,TestProc2,pszText2,0,&nID);
getchar();//让主线程不结束,能让子线程执行
return 0;
}
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性
BOOL bManualReset, //事件重置(复位)方式,TRUE手动,FALSE自动,复位的意思就是将事件句柄从有信号变为无信号,如果将事件句柄从无信号变为有信号这叫做“触发”
BOOL bInitialState, //事件初始状态,TRUE有信号,FALSE无信号
LPTSTR lpName //事件命名
); //创建成功返回事件句柄
WaitForSingleObject/WaitForMultipleObject
BOOL SetEvent(
HANDLE hEvent //handle of event
);
BOOL ResetEvent(
HANDLE hEvent //handle of event
);
CloseHandle
注意
:小心事件的死锁
示例代码:用线程2控制线程1的打印速度,每秒打印一次
#include
#include
HANDLE g_hEvent = 0;//接收事件句柄
DWORD CALLBACK PrintProc(LPVOID pParam){
while(1){
//1.等到事件有信号
WaitForSingleObject(g_hEvent, INFINITE);
//3.将事件设置为无信号
ResetEvent(g_hEvent);
printf("*************\n");
}
return 0;
}
DWORD CALLBACK CtrlProc(LPVOID pParam){
while(1){
Sleep(1000);
//2.设置事件有信号
SetEvent(g_hEvent);
}
return 0;
}
int main(){
g_hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
DWORD nID = 0;
HANDLE hThread[2] = {0};
hThread[0] = CreateThread(NULL,0,PrintProc,NULL,0,&nID);
hThread[1] = CreateThread(NULL,0,CtrlProc,NULL,0,&nID);
WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
return 0;
}
类似于事件,解决通知的相关问题。但是提供一个计数器,可以设置次数。
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //安全属性
LONG lInitialCount,//初始化信号量数量
LONG lMaximumCount,//信号量的最大值(即lInitialCount<=lMaximumCount)
LPCTSTR lpName //命名
); //创建成功返回信号量句柄
WaitForSingleObject / WaitForMultipleObjects
每等待通过一次,信号量的信号减1,直到为0阻塞BOOL ReleaseSemaphore(
HANDLE hSemaphore,//信号量句柄
LONG lReleaseCount, //释放数量
LPLONG lpPreviousCount//释放前原来信号量的数量(即设置新的数量前可用的信号量个数),可以为NULL
);
CloseHandle
示例代码1:让信号量控制打印的次数
#include
#include
HANDLE g_hSema = 0;
DWORD CALLBACK TestProc(LPVOID pParam){
while(1){
WaitForSingleObject(g_hSema, INFINITE);
printf("********\n");
}
}
int main(){
g_hSema = CreateSemaphore(NULL,3,10,NULL);
DWORD nID = 0;
HANDLE hThread = CreateThread(NULL,0,TestProc,NULL,0,&nID);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(g_hSema);
return 0;
}
```![在这里插入图片描述](https://img-blog.csdnimg.cn/f4c2e83a5ae24d8b9a84522e0bf8139d.png#pic_center)
示例代码2:让信号量控制打印3次,然后按回车键再打印5次
```c
#include
#include
HANDLE g_hSema = 0;
DWORD CALLBACK TestProc(LPVOID pParam){
while(1){
WaitForSingleObject(g_hSema, INFINITE);
printf("********\n");
}
}
int main(){
g_hSema = CreateSemaphore(NULL,3,10,NULL);
DWORD nID = 0;
HANDLE hThread = CreateThread(NULL,0,TestProc,NULL,0,&nID);
getchar();//按回车键
ReleaseSemaphore(g_hSema,5,NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(g_hSema);
return 0;
}
示例代码:测试重设值如果大于最大预设值会发生什么现象
#include
#include
HANDLE g_hSema = 0;
DWORD CALLBACK TestProc(LPVOID pParam){
while(1){
WaitForSingleObject(g_hSema, INFINITE);
printf("********\n");
}
}
int main(){
g_hSema = CreateSemaphore(NULL,3,10,NULL);
DWORD nID = 0;
HANDLE hThread = CreateThread(NULL,0,TestProc,NULL,0,&nID);
getchar();//按回车键
ReleaseSemaphore(g_hSema,11,NULL);//这里故意让重设值大于最大预设值10
WaitForSingleObject(hThread, INFINITE);
CloseHandle(g_hSema);
return 0;
}