Windows线程开发

Windows线程开发

Unit01线程

  • 加锁机制:原子锁、互斥锁
  • 协调机制:信号量、事件

01线程基础

  • Windows线程是可以执行的代码的实例。系统是以线程为单位调度程序。一个程序中可以有多个线程,实现多任务的处理
  • Windows线程的特点:
    • 线程都具有一个ID
    • 每个线程都具有自己的内存栈
    • 同一进程中的线程使用同一个地址空间(栈空间例外)
  • 线程的调度:将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程
    • 线程轮询:线程A->线程B->线程A…(分时复用)

02创建线程

  • 创建线程
HANDLE CreateThread(
	LPSECURITY_ATTRIBUTES lpsa,//安全属性(在windows开发中安全属性都是废弃的参数)
	DWORD cbStack, //线程栈的大小(按照1M对齐)
	LPTHREAD_START_ROUTINE lpStartAddr,//线程处理函数的函数地址
	LPVOID lpvThreadParam, //传递给线程处理函数的参数
	DWORD fdwCreate,//线程的创建方式(立即执行方式,挂起方式)
	LPDWORD lpIDThread//创建成功,返回线程的ID
); //创建成功,返回线程句柄

Windows线程开发_第1张图片- 定义线程处理函数

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;
}

03线程挂起/销毁

  • 挂起(休眠)
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;
}

Windows线程开发_第2张图片

  • 销毁线程
    • 结束指定线程
    • 结束函数所在的线程
//结束指定线程(相当于他杀)
BOOL TerminateThread(
	HANDLE hThread,//handle of thread
	DWORD dwExitCode//exit code
);
//结束函数所在的线程(相当于线程自杀)
VOID ExitThread(
	DWORD dwExitCode//exit code for this thread(这个参数没有实际的意义)
); 

04线程相关操作

  • 获取当前线程的ID
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;
}

Unit02线程同步之原子锁

01原子锁

  • 相关问题:多个线程对统一数据进行原子操作,会产生结果丢失。比如执行++运算时
#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;
}

Windows线程开发_第3张图片

  • 错误代码分析:当线程A执行g_value++时,如果线程切换时间正好是线程A将只保存到g_value之前,线程B继续执行g_value++,那么当线程A再次被切换回来之后,会将原来线程A保存的值保存到g_value上,线程V进行的加法操作将会被覆盖
    Windows线程开发_第4张图片
  • 使用原子锁函数
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;
}

Windows线程开发_第5张图片

Unit03线程同步之互斥

01互斥

  • 相关问题:多线程下代码或资源(临界资源)的共享使用

互斥的使用

  • 创建互斥
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;
}

Windows线程开发_第6张图片

  • 解决后的代码
#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;
}

Windows线程开发_第7张图片

Unit04线程同步之事件

事件

相关问题

  • 程序(线程)之间的通知的问题

事件的使用

  • 创建事件
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;
}

Unit05线程同步之信号量

01信号量

相关问题

类似于事件,解决通知的相关问题。但是提供一个计数器,可以设置次数。

信号量的使用

  • 创建信号量
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;
}

Windows线程开发_第8张图片

示例代码:测试重设值如果大于最大预设值会发生什么现象

#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;
}

Windows线程开发_第9张图片

你可能感兴趣的:(windows,c语言,c++)