Windows线程开发

Posted muxisuibi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Windows线程开发相关的知识,希望对你有一定的参考价值。

Windows线程开发

1.线程基础

  • Windows线程是可以执行的代码实例。系统十一线程为单位调度程序。一个程序当中可以有多个线程,实现多个任务的处理。

  • Windows线程的特点:

    1. 线程都具有1个ID
    2. 每个线程都具有自己的内存栈
    3. 同一进程中的线程使用同一个地址空间
  • 线程的调度

    操作系统将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程。线程轮询:线程A->线程B->线程A......

2.创建线程

  • 创建线程

    HANDLE CreateThread(
    	LPSECURITY_ATTRIBUTES lpThreadAttributes, //安全属性
        SIZE_T dwStackSize,            //线程栈的大小
        LPTHREAD_START_ROUTINE lpStartAddress, //线程处理函数的函数地址
        LPVOID lpParameter,           //传递给线程处理函数的参数
        DWORD dwCreationFlags,        //线程创建方式
        LPDWORD lpThreadId            //创建成功,返回线程的ID
    );创建成功,返回线程句柄
    
  • 定义线程处理函数

    DWORD WINAPI ThreadProc(
    	LPVOID lpParameter //创建线程时,传递给线程的参数
    );
    

例子:

#include <Windows.h>
#include <stdio.h>
DWORD CALLBACK ThreadProc(LPVOID param){
	char* pszText = (char*) param;
	while(1){
		printf("%s\\n",pszText);
		Sleep(1000);
	}
	return 0;
}
int main(){
	DWORD pid = 0;
	char* pszText="******";
	HANDLE hThread = CreateThread(NULL,0,ThreadProc,pszText,0,&pid);
	getchar();
	return 0;
}

3.线程挂起/销毁

  • 挂起

    DWORD SuspendThread(
    	HANDLE hThread  //handle to thread
    );
    
  • 唤醒

    DWORD ResumeThread(
    	HANDLE hThread //handle to thread
    );
    

例子:

#include <Windows.h>
#include <stdio.h>
DWORD CALLBACK ThreadProc(LPVOID param){
	char* pszText = (char*) param;
	while(1){
		printf("%s\\n",pszText);
		Sleep(1000);
	}
	return 0;
}

DWORD CALLBACK ThreadProc2(LPVOID param){
	char* pszText = (char*) param;
	while(1){
		printf("%s\\n",pszText);
		Sleep(1000);
	}
	return 0;
}
int main(){
	DWORD pid = 0;
	char* pszText="<<<<<<";
	HANDLE hThread = CreateThread(NULL,0,ThreadProc,pszText,0,&pid);  //创建的线程立即执行
	char* pszText2 = ">>>>>>";
	HANDLE hThread2 = CreateThread(NULL,0,ThreadProc2,pszText2,CREATE_SUSPENDED,&pid); //创建的线程挂起
	getchar();
	SuspendThread(hThread);
	ResumeThread(hThread2);
	getchar();
	return 0;
}

4.线程相关操作

  • 结束指定线程

    BOOL TerminateThread(
    	HANDLE hTread,  //handle to thread
        DWORD dwExitCode  //exit code
    );
    
  • 结束函数所在的线程

    VOID ExitThread(
    	DWORD dwExitCode //exit code for this thread
    );
    
  • 获取当前线的ID

    GetCurrentThreadId();
    
  • 获取当前线程的句柄

    GetCurrentThread();
    
  • 等候单个句柄有信号

    VOID WaitForSingleObject(
    	HANDLE handle,  //句柄BUFF的地址
        DWORD dwMillseconds //等候时间INFINITE(无限大)
    );
    
  • 同时等候多个句柄有信号

    DWORD WaitForMultipleObjects(
    	DWORD nCount, //句柄数量
        CONST HANDLE *lpHandles, //句柄BUFF的地址
        BOOL bWaitAll, //等候方式
        DWORD dwMillisenconds //等候时间INFINITE
    );
    bWaitAll - 等候方式:
        TRUE - 表示所有句柄都有信号,才结束等候
        FALSE - 表示句柄中只要有1个有信号,就结束等候
    

5.线程同步

5.1 原子锁

  • 相关问题

    ​ 多个线程对同一个数据进行原子操作,会产生结果丢失。比如执行++运算时。

  • 错误代码分析

    ​ 当线程A执行g_value++时,如果线程切换时间正好是线程A将值保存到g_value之前线程B继续执行g_value++,那么当线程A再次切换回来之后,会将原来线程A保存的值保存到g_value上,线程B进行的加法操作被覆盖。

  • 使用原子锁函数

    ​ InterlockedIncrement

    ​ InterlockedDecrement

    ​ InterlockedCompareExchange

    ​ InterlockedExchange

    ​ ...

    原子锁的实现:直接对数据所在的内存操作,并且 在任何一个瞬间只能有一个线程访问。

5.2 互斥

  • 相关的问题

    ​ 多线程下代码或资源的共享使用。

  • 互斥的使用

    1. 创建互斥

      HANDLE CreateMutex(
      	LPSECURITY_ATTRIBUTES lpMutexAttributes, //安全属性
          BOOL bInitialOwner, //初始的拥有者 TRUE/FALSE
          LPCTSTR lpName //命名
      ); 创建成功返回互斥句柄
      
    2. 等候互斥

      ​ WaitFor... 互斥的等候遵循谁先等候谁先获取。

    3. 释放互斥

      BOOL ReleaseMutex(
      	HANDLE hMutex //handle to mutex
      );
      
    4. 关闭互斥句柄

      CloseHandle(
      	HANDLE hMutex
      );
      

例子:

#include <Windows.h>
#include <stdio.h>
HANDLE g_hMutex = 0; //获得互斥句柄
DWORD CALLBACK ThreadProc(LPVOID param){
	char* pszText = (char*) param;
	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 ThreadProc2(LPVOID param){
	char* pszText = (char*) param;
	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);
	DWORD pid = 0;
	char* pszText="<<<<<<";
	HANDLE hThread = CreateThread(NULL,0,ThreadProc,pszText,0,&pid);  //创建的线程立即执行
	char* pszText2 = ">>>>>>";
	HANDLE hThread2 = CreateThread(NULL,0,ThreadProc2,pszText2,0,&pid);
	getchar();
	CloseHandle(g_hMutex);
	return 0;
}

5.3 事件

  • 相关问题

    ​ 程序之间的通知的问题。

  • 事件的使用

    1. 创建事件

      HANDLE CreateEvent(
      	LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性
          BOOL bManualReset,
           //事件重置(复位)方式,TRUE手动,FALSE自动
          BOOL bInitialState, //事件初始状态,TRUE有信号
          LPCTSTR lpName //事件命名
      );创建成功返回事件句柄
      
    2. 等候事件

      WaitForSingleObject/WaiteForMultipleObjects

    3. 触发事件(将事件设置成有信号状态)

      BOOL SetEvent(
      	HANDLE hEvent //handle to event
      );
      
    4. 复位事件(将事件设置成无信号状态)

      BOOL ResetEvent(
      	HANDLE hEvent //handle to event
      );
      
    5. 关闭事件

      CloseHandle(HANDLE hEvent);
      

    小心事件的死锁

    例子:

    #include <Windows.h>
    #include <stdio.h>
    HANDLE g_hEvent = 0; //获得事件句柄
    DWORD CALLBACK PrintProc(LPVOID param){
    	while(1){
    		WaitForSingleObject(g_hEvent,INFINITE);
    		ResetEvent(g_hEvent);
    		printf("*********\\n");
    	}
    	return 0;
    }
    
    DWORD CALLBACK CtlProc(LPVOID param){
    	while(1){
    		Sleep(1000);
    		SetEvent(g_hEvent);
    	}
    	return 0;
    }
    int main(){
    	g_hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
    	DWORD pid = 0;
    	HANDLE hThread[2] = {0};
    	hThread[0] = CreateThread(NULL,0,PrintProc,NULL,0,&pid);  
    	hThread[0] = CreateThread(NULL,0,CtlProc,NULL,0,&pid);
    	WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
    	getchar();
    	CloseHandle(g_hEvent);
    	return 0;
    }
    

5.4 信号量

  • 相关的问题

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

  • 信号量的使用

    1. 创建信号量

      HANDLE CreateSemaphore(
      	LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //安全属性
          LONG lInitialCount, //初始化信号量数量
          LONG lMaximumCount, //信号量的最大值
          LPCTSTR lpName //命名
      ); 创建成功返回信号量句柄
      
    2. 等候信号量

      WaitFor... 每等候通过一次,信号量的信号减1,直到为0阻塞

    3. 给信号量指定计数值

      BOOL ReleaseSemaphore(
      	HANDLE hSemaphore, //信号量句柄
          LONG lReleaseCount, //释放数量
          LPLONG lpPreviousCount //释放前原来信号量的数量,可以为NULL
      );
      
    4. 关闭句柄

      CloseHandle(HANDLE handle);
      
    #include <Windows.h>
    #include <stdio.h>
    HANDLE g_hSema = 0; //信号量句柄
    DWORD CALLBACK TestProc(LPVOID param){
    	while(1){
    		WaitForSingleObject(g_hSema,INFINITE);
    		printf("*********\\n");
    	}
    	return 0;
    }
    
    int main(){
    	g_hSema = CreateSemaphore(NULL,3,10,NULL);
    	DWORD pid = 0;
    	HANDLE hThread = CreateThread(NULL,0,TestProc,NULL,0,&pid);  
    	getchar();
    	ReleaseSemaphore(g_hSema,5,NULL);
    	WaitForSingleObject(hThread,INFINITE);
    	CloseHandle(g_hSema);
    	return 0;
    }
    

以上是关于Windows线程开发的主要内容,如果未能解决你的问题,请参考以下文章

java开发的项目案例,大厂内部资料

Windows线程开发

Windows内核开发-Windows内部概述-2-

newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段

线程同步(windows平台):临界区

20160226.CCPP体系详解(0036天)