zoukankan      html  css  js  c++  java
  • 27.windows线程

    1.windows线程

      windows线程是可以执行的代码的实例。系统以线程为单位调度程序。

      一个程序中可以有多个线程,实现多任务处理。

    2.windows线程的特点

      (1)线程都有一个ID

      (2)线程具有自己的安全属性

      (3)每个线程都有自己的内存栈

      (4)每个线程都有自己的寄存器信息

    3.进程多任务和线程多任务

      进程多任务:每个进程都使用私有的地址空间

      线程多任务:进程内的多个线程使用同一个地址空间

      线程调用:将CPU的执行时间划分为时间片,依次根据时间片执行不同的线程

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

    4.线程使用

      (1)定义线程处理函数

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

      (2)创建线程

         HANDLE  CreateThread( LPSECURITY_ATTRIBUTES      lpThreadAttributes,  //安全属性

                                                     SIZE_T                                 dwStackSize,          //线程栈大小(小于1M都按1M算)

                                                     LPTHREAD_START_ROUTINE   lpStartAddress,      //线程处理函数地址

                                                     LPVOID                                 lpParameter,           //传给线程处理函数的参数

                                                     DWORD                                dwCreationFlags,     //创建方式

                                                     LPDWORD                             lpThreadId);           //创建成功,返回线程ID

         创建成功返回线程句柄

         创建方式(dwCreationFlags):0 - 创建之后线程立即执行

                                          CREATE_SUSPENDED - 创建之后线程属于挂起(休眠)状态

      (3)结束线程

         //结束指定线程

         BOOL  TerminateThread( HANDLE   hThread,         //线程句柄

                                                      DWORD   dwExitCode);   //退出码

         //结束所在线程

         VOID  ExitThread( DWORD  dwExitCode );

      (4)关闭线程句柄

         CloseHandle(将线程句柄置为-1,只是这个句柄不能用了,跟线程状态没有任何关系)

      (5)线程的挂起和执行

         //挂起

         DWORD  SuspendThread( HANDLE  hThread );

         //执行

         DWORD  ResumeThread( HANDLE  hThread );

      (6)线程的信息

         //获得当前线程的ID

         DWORD  GetCurrentThreadId( VOID ); 

         //获得当前线程的句柄

         HANDLE  GetCurrentThread( VOID );

         通过指定ID的线程获得线程句柄

         HANDLE OpenThread( DWORD   dwDesiredAccess,  //访问权限

                                                  BOOL       bInheritHandle,     //继承标识

                                                  DWORD   dwThreadId);        //线程ID

    5.多线程问题

      线程A -> 线程B -> 线程A ...,例如:线程A打印"******** ",线程B打印"-------- ",会出现错乱情况:

      ****----****

      --********

      -------*****

      .......,那么,出现的原因是:

      当线程A执行printf输出时,如果线程A的执行时间结束,系统会将线程A的相关信息(栈、寄存器)压栈保护,

      同时将线程B相关信息恢复,然后执行线程B,线程B继续输出字符。由于线程A正在输出字符,线程B

      会继续输出,画面字符会产生混乱。

      相关代码:

    #include "stdafx.h"
    #include "windows.h"
    #include "stdio.h"
    
    
    DWORD CALLBACK TestProc(LPVOID pParam)
    {
        char *pszText = (char*)pParam;
        while (1)
        {
            printf("%s
    ", pszText);
            Sleep(1000);
        }
        return 0;
    }
    DWORD CALLBACK TestProc2(LPVOID pParam)
    {
        char *pszText = (char*)pParam;
        while (1)
        {
            printf("%s
    ", pszText);
            Sleep(1000);
        }
        return 0;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        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);
        getchar();
        SuspendThread(hThread);
        ResumeThread(hThread2);
        getchar();
        return 0;
    }
    View Code

    6.线程同步技术

      原子锁、临界区、互斥(用于加锁)

      事件、信号量、可等候定时器(用于线程同步,线程之间协调工作)

      (1)等候函数

         //等候单个

         DWORD  WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );

         //等候多个

         DWORD  WaitForMultipleObjects( DOWRD              nCount,             //句柄数量

                                                                  CONST HANDLE   *lpHandles,        //句柄buff地址

                                                                  BOOL                  bWaitAll,            //等候方式

                                                                  DWORD              dwMilliseconds);  //等候时间

         等候方式:TRUE - 所有句柄都有信号,才结束等候

              FALSE - 只要有一个有信号,就结束等候

      (2)原子锁

         a.相关问题

           多个线程同时对一个数据进行原子操作,会产生结果丢失。比如++运算时,线程A执行g_nValue++时,如果线程切换时间正好在线程A

           将值保存到g_nValue之前,线程B继续执行g_nValue++。那么,当线程A再次切换回来之后,会将原来线程A还未保存的值保存到

           g_nValue上,线程B进行的操作就被覆盖。

           b.使用

           原子锁 - 对单条指令的操作

           API:LONG InterlockedIncrement( LPLONG volatile lpAddend );

               LONG InterlockedDecrement (LPLONG volatile lpAddend );

               LONG InterlockedCompareExchange( LPLONG volatile Destination, LONG Exchange, LONG Comperand );

               LONG InterlockedExchange( LONG volatile Target, LONG Value );

               ...

         c.原子锁的实现

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

      相关代码:

    #include "stdafx.h"
    #include "windows.h"
    #include "stdio.h"
    
    DWORD g_nValue = 0;
    
    DWORD CALLBACK TestProc1(LPVOID pParam)
    {
        for (DWORD i = 0; i < 100000000; i++)
        {
            //g_nValue++;
            InterlockedIncrement(&g_nValue);//原子锁函数
        }
        return 0;
    }
    DWORD CALLBACK TestProc2(LPVOID pParam)
    {
        for (DWORD i = 0; i < 100000000; i++)
        {
            //g_nValue++;
            InterlockedIncrement(&g_nValue);//原子锁函数
        }
        return 0;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        DWORD nID = 0;
        //线程在执行时无信号,执行结束有信号
        HANDLE hThread[2] = { 0 };
        hThread[0] = CreateThread(NULL, 0, TestProc1, NULL, 0, &nID);
        hThread[1] = CreateThread(NULL, 0, TestProc2, NULL, 0, &nID);
        //防止主线程结束  getchar();
        //等待所有子线程结束
        WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
        printf("%d
    ", g_nValue);
        CloseHandle(hThread[0]);
        CloseHandle(hThread[1]);
        return 0;
    }
    View Code

      (3)临界区

         a.相关问题

           printf输出混乱,多线程情况下同时使用一段代码。临界区可以锁定一段代码,防止多个线程同时使用该段代码。

         b.使用

           初始化一个临界区

             VOID  InitializeCriticalSection( LPCRITICAL_SECTION  lpCriticalSection );

           进入临界区

             添加到被锁定的代码之前

             VOID  EnterCriticalSection( LPCRITICAL_SECTION  lpCriticalSection );

           离开临界区

             添加到被锁定的代码之后

             VOID  LeaveCriticalSection( LPCRITICAL_SECTION  lpCriticalSection );  

           删除临界区

             VOID  DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection );

         c.临界区和原子锁的区别

           原子锁 - 锁定单条指令

           临界区 - 锁定单条或多条指令

      相关代码:

    #include "stdafx.h"
    #include "windows.h"
    #include "stdio.h"
    
    
    CRITICAL_SECTION cs = { 0 };
    
    DWORD CALLBACK TestProc1(LPVOID pParam)
    {
        while (1)
        {
            EnterCriticalSection(&cs);  //进入临界区
            printf("********
    ");
            Sleep(1000);
            LeaveCriticalSection(&cs);  //离开临界区
        }    
        return 0;
    }
    DWORD CALLBACK TestProc2(LPVOID pParam)
    {
        while (1)
        {
            EnterCriticalSection(&cs);  //进入临界区
            printf("--------
    ");
            Sleep(1000);
            LeaveCriticalSection(&cs);  //离开临界区
        }    
        return 0;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        //初始化临界区
        InitializeCriticalSection(&cs);
        DWORD nID = 0;
        HANDLE hThread[2] = { 0 };
        //创建子线程
        hThread[0] = CreateThread(NULL, 0, TestProc1, NULL, 0, &nID);
        hThread[1] = CreateThread(NULL, 0, TestProc2, NULL, 0, &nID);
        //等待子线程结束
        WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
        //关闭线程句柄
        CloseHandle(hThread[0]);
        CloseHandle(hThread[1]);
        //删除临界区
        DeleteCriticalSection(&cs);
        return 0;
    }
    View Code

      (3)互斥

         a.相关问题

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

         b.使用

           创建互斥

             HANDLE  CreateMutex( LPSECURITY_ATTRIBUTES  lpMutexAttributes,  //安全属性

                                                               BOOL                                bInitialOwner,        //初始的拥有者

                                                               LPCTSTR                           lpName);               //名称

             创建成功返回互斥句柄

             互斥句柄是可等候句柄(进程句柄、线程句柄是互斥句柄),当任何线程都不拥有互斥句柄时,有信号;被线程拥有时,有信号。

             出事拥有者:TRUE - 创建互斥的线程拥有互斥

                   FALSE - 创建时没有线程拥有互斥

           等候互斥

             WaitForMultipleObjects

             互斥的等候遵循谁先等候谁线获取

           释放互斥

             BOOL  ReleaseMutex( HANDLE  hMutex );

           关闭互斥句柄

             CloseHandle

         c.互斥和临界区的区别

           临界区 - 用户态,自行效率高,只能在同一个进程中使用

           互斥 - 内核态,执行效率低,可以通过命名的方式跨进程使用

      相关代码:

    #include "stdafx.h"
    #include "windows.h"
    #include "stdio.h"
    
    
    HANDLE g_hMutex = 0;  //接收互斥句柄
    
    DWORD CALLBACK TestProc1(LPVOID pParam)
    {
        while (1)
        {
            //如果g_hMutex有信号,通过阻塞同时将g_hMutex置为无信号
            //如果g_hMetex无信号,则阻塞
            WaitForSingleObject(g_hMutex, INFINITE);
            printf("********
    ");
            Sleep(1000);
            ReleaseMutex(g_hMutex);
        }
        return 0;
    }
    DWORD CALLBACK TestProc2(LPVOID pParam)
    {
        while (1)
        {
            WaitForSingleObject(g_hMutex, INFINITE);
            printf("--------
    ");
            Sleep(1000);
            ReleaseMutex(g_hMutex);
        }
        return 0;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        //创建互斥
        g_hMutex = CreateMutex(NULL, FALSE, NULL);
    
        DWORD nID = 0;
        HANDLE hThread[2] = { 0 };
        hThread[0] = CreateThread(NULL, 0, TestProc1, NULL, 0, &nID);
        hThread[1] = CreateThread(NULL, 0, TestProc2, NULL, 0, &nID);
        WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
        CloseHandle(hThread[0]);
        CloseHandle(hThread[1]);
        CloseHandle(g_hMutex);
        return 0;
    }
    View Code

      (4)事件

         a.相关问题

           程序之间的通知问题

         b.使用事件

           创建事件

             HANDLE  CreateEvent( LPSECURITY_ATTRIBUTES   lpEventAttributes,  //安全属性

                                                              BOOL                                 bManualReset,       //事件复位方式,TRUE表示手动,FALSE表示自动

                                                              BOOL                                 bInitialState,         //事件初始状态,TRUE表示有信号

                                                              LPCTSTR                            lpName);              //事件名称

             成功返回事件句柄,事件句柄是可等候句柄,有无信号,自己控制

           等候事件

             WaitForSingleObject

           触发事件

             //将事件设置成有信号状态

             BOOL  SetEvent( HANDLE  hEvent );

             //将事件设置成无信号状态

             BOOL  ResetEvent( HANDLE  hEvent );

           关闭事件

             CloseHandle

           注意:小心事件的死锁

           自动复位方式是在WaitForSingleObject( handle, INFINITE )函数中实现的

           {

             ......

             阻塞代码

             ......

             //判断handle是否为事件句柄

             if  ( 事件句柄 )

             {

               //通过事件句柄查看事件的复位方式

               if  ( 手动 ) {  //什么都不干  }

               if  ( 自动 ) {  ResetEvent( handle );  }

             }

           }

      相关代码:

    #include "stdafx.h"
    #include "windows.h"
    #include "stdio.h"
    
    HANDLE g_hEvent = 0;
    
    DWORD CALLBACK PrintProc(LPVOID pParam)
    {
        while (1)
        {
            WaitForSingleObject(g_hEvent, INFINITE);
            ResetEvent(g_hEvent);  //事件复位(事件变成无信号)
            printf("********
    ");        
        }
        return 0;
    }
    DWORD CALLBACK CtrlProc(LPVOID pParam)
    {
        while (1)
        {
            Sleep(1000);
            SetEvent(g_hEvent);  //将事件设置成有信号
        }
        return 0;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        //创建事件,手动复位,初始无信号
        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(hThread[0]);
        CloseHandle(hThread[1]);
        CloseHandle(g_hEvent);
        return 0;
    }
    View Code

      (5)信号量

         a.相关问题

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

         b.使用信号量

           创建信号量

             HANDLE  CreateSemaphore( LPSECURITY_ATTRIBUTES  lpSemaphoreAttributes,  //安全属性

                                                                      LONG                                lInitialCount,                 //信号量的初始计数值

                                                                      LONG                                lMaximumCount,           //信号量的最大值

                                                                      LPCTSTR                           lpName);                      //名称   

             创建成功返回信号量句柄(可等候句柄),计数值不为0有信号,为0无信号。  

           等候信号量

             WaitForSingleObject

             等候没通过一次,信号量计数值减1,知道为0阻塞

           释放信号量(重新设置信号量的计数值)

             BOOL  ReleaseSemaphore( HANDLE  hSemaphore,          //信号量句柄

                                                                    LONG      lReleaseCount,       //释放数量

                                                                    LPLONG   lpPreviousCount);   //原来信号量的数量,可以为NULL

           关闭句柄

             CloseHandle

      相关代码: 

    #include "stdafx.h"
    #include "windows.h"
    #include "stdio.h"
    
    HANDLE g_hSemaphore = 0;
    
    DWORD CALLBACK TestProc(LPVOID pParam)
    {
        while (1)
        {
            WaitForSingleObject(g_hSemaphore, INFINITE);
            printf("********
    ");
        }
        return 0;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        g_hSemaphore = CreateSemaphore(NULL, 3, 10, NULL);
        DWORD nID = 0;
        HANDLE hThread = CreateThread(NULL, 0, TestProc, NULL, 0, &nID);
        getchar();
        //重新设置计数值,不能超过创建时的最大计数值(10)
        ReleaseSemaphore(g_hSemaphore, 10, NULL);
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
        CloseHandle(g_hSemaphore);
        return 0;
    }
    View Code
  • 相关阅读:
    合并链表
    ImportError: cannot import name 'GMM' from 'sklearn.mixture'
    SyntaxError: invalid character in identifier
    在 jupyter notebook 中插入图片
    Atom 换行后删除前面空格,需要按2/4次退格键
    win10输入法InpuMode默认显示中文
    Visual Studio(WindowsSDK.targets(46,5): error MSB8036: 找不到 Windows SDK 版本8.1)
    atom修改注释的字体,字号,颜色
    '__int64' has not been declared
    VMware 打开虚拟机库
  • 原文地址:https://www.cnblogs.com/csqtech/p/5671382.html
Copyright © 2011-2022 走看看