• Windows线程


    Windows线程
    线程基础
    • Windows线程是可以执行的代码的实例。

      系统是以线程为单位调度程序,一个程序中可以有多个线程,实现多任务的处理。主线程只能有一个。

    • Windows线程的特点

      1. 线程都具有1个ID
      2. 每个线程都具有自己的内存栈,其余的共享。
      3. 同一进程中的线程使用同一个地址空间。(除了栈空间)
    • 线程的调度

      操作系统将CPU的执行时间划分为时间片,依次根据时间片执行不同的线程。

      线程轮询: 线程A -> 线程B ->线程A …

    创建线程
    • 创建线程

      HANDLE CreateThread(
      	LPSECURITY_ATTRIBUTES lpThreadAttributes , // 安全属性,已废弃, NULL
          SIZE_T dwStackSize, // 线程栈的大小  按1M对齐,最少1M
          LPTHREAD_START_ROUTINE lpStartAddress, // 线程处理函数的函数地址,自己定义由系统调用
          LPVOID lpParameter, // 传递给线程处理函数的参数
          DWORD dwCreationFlags, // 线程的创建方式(立即执行&挂起方式)
          // 0 -> 立即启动
          // CREATE_SUSPENDED -> 挂起
          LPDWORD lpThreadId // 创建成功,返回线程的ID
      ); // 创建成功,返回线程句柄  ( 线程句柄和线程ID都可以代表线程 )
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • 定义线程处理函数

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

    Demo

    #include 
    #include 
    DWORD CALLBACK TestProc(LPVOID pParam){
       char * pszText = (char *) pParam;
       printf("%s",pszText);
    }
    int main(){
        DWORD nID = 0;
        char * pszText = "******";
        HANDLE hThread = CreateThread(NULL,0,TestProc,pszText,0,&nID);
    	getchar();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    子线程正常执行->要保证主线程不结束

    销毁线程
    • 挂起

      DWORD SuspendThread(
      	HANDLE hThread // 线程句柄
      );
      
      • 1
      • 2
      • 3
    • 唤醒

      DWORD ResumeThread(
      	HANDLE hThread  // 线程句柄
      );
      
      • 1
      • 2
      • 3
    • 结束指定线程

      BOOL TerminateThread(
      	HANDLE hThread, // 线程句柄
          DWORD dwExitCode // 退出码,一般没用
      );
      
      • 1
      • 2
      • 3
      • 4
    • 结束函数所在的线程

      VOID ExitThread(
      	DWORD dwExitCode // 退出码,没有实际意义
      );
      // 自杀 -> 只能干掉调用的线程
      
      • 1
      • 2
      • 3
      • 4
    线程相关操作
    • 获取当前线程ID

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

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

      VOID WaitForSingleObject(
      	HANDLE handle , // 句柄BUFF的地址
          DWORD dwMilliseconds // 最长等候时间 ms
          // INFINITE 一直等待
      );
      // 有信号时会立即返回
      // 没有信号会阻塞
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      线程句柄是可等候句柄: 有信号和无信号状态

    • 同时等候多个句柄有信号

      DWORD WaitForMultipleObject(
      	DWORD nCount, // 句柄数量
          CONST HANDLE *lpHandles, // 句柄BUFF的地址
          BOOL bWaitAll, // 等候方式
          // TRUE : 所有句柄都有信号才会返回
          // FALSE: 只要有一个有信号就返回
          DWORD dwMilliseconds // 等候时间  INFINITE
      );
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      可等候句柄

    • 线程信号

      • 当线程处于执行状态时,线程无信号
      • 当线程结束那刻,线程有信号
    线程同步
    原子锁
    • 当多个线程对同一个数据进行原子操作,会产生结果丢失,比如算术运算

    • 使用原子锁函数

      InterlockedIncrement   // ++ 操作符
      InterlockedDecrement   // --
      InterlockedCompareExchange   // 三目运算符 
      InterlockedExchange    // = 赋值
      
      • 1
      • 2
      • 3
      • 4

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

    • 只能对运算符加锁,效率高,但是麻烦

    互斥锁
    • 相关问题

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

    • 互斥的使用

      1. 创建互斥

        HANDLE CreateMutex(
        	LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全数据(NULL)
            BOOL bInitialOwner, // 初始的拥有者 TRUE/FALSE
            // TRUE: 哪个线程创建哪个线程拥有
            // FALSE: 都不拥有
            LPCTSTR lpName // 命名
        ); // 创建成功返回互斥句柄
        // 互斥句柄,也是可等候句柄
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 在任何时间点上只有一个线程拥有互斥, 独占性和排他性

        • 当任何线程都不拥有互斥,互斥有信号,任何一个线程拥有互斥,互斥就没有信号

      2. 等候互斥

        WaitFor… 函数

        互斥的等候遵循谁先等候谁先获得

        也就是加锁

      3. 释放互斥

        BOOL ReleaseMutex(
        	HANDLE hMutex // 互斥锁句柄
        );
        
        • 1
        • 2
        • 3
      4. 关闭互斥句柄

        CloseHandle
        
        • 1

    demo

    #include 
    #include 
    
    HANDLE hmutex; // 接收互斥句柄 
    
    DWORD CALLBACK TestProc(LPVOID pParam){
       char * pszText = (char *) pParam;
       int i ;
       while(1){
       	    WaitForSingleObject(hmutex,INFINITE);
    	    for(i = 0;i<strlen(pszText);i++){
    	   		printf("%c",pszText[i]);
    	   		Sleep(125);
    	   }
    	   printf("\n");
    	   ReleaseMutex(hmutex); 
       } 
    }
    int main(){
    	hmutex = CreateMutex(NULL,FALSE,NULL); 
        DWORD nID1 = 0;
        DWORD nID2 = 0;
        char * pszText1 = "******";
        char * pszText2 = "------";
        HANDLE hThread1 = CreateThread(NULL,0,TestProc,pszText1,0,&nID1);
        HANDLE hThread2 = CreateThread(NULL,0,TestProc,pszText2,0,&nID2);
    	getchar();
    	CloseHandle(hmutex);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    事件
    • 相关问题

      线程之间的通知问题

    • 事件的使用

      1. 创建事件

        HANDLE CreateEvent(
        	LPSEURITY_ATTRIBUTES lpEventAttributes, // 安全属性
            BOOL bManualReset, // 事件重置(复位)方式 有信号 -> 无信号
            // TRUE  手动
            // FALSE 自动 --> 读取信号一次就会自动复位
            // 触发 无信号 ->有信号
            BOOL bInitialState ,// 事件初始状态,TRUE 有信号
            LPCTSTR lpName // 事件命名,可以为空
        );// 创建成功返回事件句柄
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9

        可等候句柄

        事件的有信号无信号可控制

      2. 等候事件

        WaitForSingleObject  / WaitForMultipleObjects
        // 自动复位方式,会自动复位
        
        • 1
        • 2
      3. 触发事件( 将事件设置为有信号状态)

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

        BOOL ResetEvent(
        	HANDLE hEvent
        );
        
        • 1
        • 2
        • 3
      5. 关闭事件

        CloseHandle
        
        • 1

    要小心事件的死锁问题

    demo

    #include 
    #include 
    HANDLE g_hEvent = 0; // 接收事件句柄
    
    DWORD CALLBACK PrintProc(LPVOID pParam){
    	while(1){
    		WaitForSingleObject(g_hEvent,INFINITE); // 等待信号 
    		ResetEvent(g_hEvent); 
    		printf("......\n");
    	}
    }
    
    DWORD CALLBACK CtrlProc(LPVOID pParam){
    	while(1){
    		Sleep(1000);
    		SetEvent(g_hEvent);
    	}
    } 
    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);
    	CloseHandle(g_hEvent);
    	return 0; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    信号量
    • 相关的问题

      作用类似于事件,解决通知的相关问题。

      提供一个计数器,可以设置次数

    • 信号量的使用

      1. 创建信号量

        HANDLE CreateSemaphore(
        	LPSECTRITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性
            LONG lInitialCount, // 初始化信号量数量
            // 信号量计数值为0时,没有信号
            // 信号量计数值不为0,有信号
            LONG lMaximumCount, // 信号量最大个数
            LPCTSTR lpName // 命名
        );// 创建成功返回信号量句柄
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8

        可等候句柄

      2. 等候信号量

        WaitFor ...
        // 每等候通过一次,信号量的信号减1,直到为0阻塞
        
        • 1
        • 2
      3. 给信号量指定计数值

        BOOL ReleaseSemaphore(
        	HANDLE hSemaphore, // 信号量句柄
            LONG lReleaseCount ,// 释放数量
            LPLONG lpPreviousCount 
            // 返回的信息,返回释放前原来信号量的数量,可以为NULL
        );
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
      4. 关闭句柄

        CloseHandle
        
        • 1

    demo

    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    使用Mapster实现双向映射,解放搬砖体力活
    南美巴西市场最全分析开发攻略,收藏一篇就够了
    定时器方案,红黑树,时间轮
    深入了解PHP中的经典一句话木马和变量传递漏洞
    想开发DAYU200,我教你
    Git操作
    JAVA全局异常处理
    周期性触发的自定义触发器
    水果叠叠乐
    [要素察觉]C语言五讲:指针
  • 原文地址:https://blog.csdn.net/first_bug/article/details/127869170