zoukankan      html  css  js  c++  java
  • MFC中的多线程

      程序是计算机指令的几何,以文件的形式存在磁盘上。进程被定义为正在运行的程序的实例,是在进行地址空间中的一次执行活动。一个程序可以对应多个进程,如可以通过打开多个Word程序,每个word的应用就是一个进程。同时一个进程可以访问多个程序。进程是系统资源申请、调度、运行的独立单位。程序不占用系统的运行资源。

       进程由两部分组成:(1)操作系统用管理进程的内核对象,一个OS内部分配的一个内存块(数据结构),并且只对操作系统可见,进程只能通过"OS提供的API"来操作这个“内存对象”;(2)地址空间,包含进程所有可执行模块,或者DLL模块的代码和数据,以及可以用于进程动态分配的空间,如线程的栈空间Stacks,以及堆空间Heap。

      进程是线程的容器,一个进程至少拥有一个主线程,被称为“主线程”,即maiin函数线程,即主线程进入点。主线程可以创建其他线程。进程是所有线程执行环境,是真正完成代码执行。这些线程“同时”执行进程地址空间中的代码。不同的进程之间不能相互访问。同一进程的不同线程之间可以相互访问。

      线程由两部分组成:(1)线程的内核对象。 被OS创建,用于被OS管理线程的最小单位(2)线程栈Stack。用于维护执行线程过程中所需要的所有函数参数和局部变量。

      当进程创建线程时,OS首先创建“线程内核对象”,OS从进程的地址空间分配内存供其“线程栈”使用。新线程可以访问进程的内核对象的所有“进程的内核对象的所有句柄”“进程中的所有内存”“本进程中的所有线程的堆栈”。所以单个进程间的所有线程之间可以非常容易相互通信。

      由于创建线程的资源比较少,一般采用单进程多线程解决问题。即所谓的“多线程编程”艺术。不采用多进程解决问题的原因(1)创建进程的代价大,且进程之间的通信比较困难;(2)进程之间切换时代价也比较大,不同进程需要切换整个地址空间,但是线程切换的只是少量的执行环境。

      多线程编程中,多线程采用时间片轮转的方式,在宏观上实现“同时”运行。如果计算机有多CPU或者多核,则可以真正实现多线程编程,且真正并行编程。

      创建线程函数的WinOSAPI:CreateThread(),此函数创建一个线程,函数原型如下:

    static HANDLE CreateThread(
       LPSECURITY_ATTRIBUTES lpsa,
       DWORD dwStackSize,
       LPTHREAD_START_ROUTINE pfnThreadProc,
       void* pvParam,
       DWORD dwCreationFlags,
       DWORD* pdwThreadId 
    ) throw( );
      lpsa:  新线程的安全特性。如果为NULL,则采用默认的安全性。
      dwStackSize  新线程的堆栈大小。即线程可以将多少地址空间用于自己的栈,以字节Byte为单位。OS会把此值四舍五入为一个合理的页面大小。一般X86使用页面大小4KB(WinOS需要保证分配堆栈是页面大小的整倍数)。
      pfnThreadProc  新线程的线程过程。即指向一个函数的指针,这个将由新创建的线程执行,即表型县县城的其实地址,即此线程的入口地址(同主线程的Main函数一样),函数名自定义,但是需要采用以下形式进行声明,入口函数的参数LPVOID的类型,返回DWORD类型。
    DWORD WINAPI ABCThreadProcABC( _In_ LPVOID lpParameter );
      pvParam  将传递的参数传递给线程过程。  可以是一个数值,或者是指向其他信息的指针。
      dwCreationFlags  创建标志(0个或CREATE_SUSPENDED)。  即如果为CREATE_SUSPENDED,则线程创建后处于暂停状态,指导程序调用ResumeThread,开始执行。如果为0,则新线程创建后立即执行。
      pdwThreadId  [out] ,若成功,接收新创建的线程的线程ID ,DWORD变量的地址。  在Win95或者后的,必须执行一个变量来接受新创建线程的ID。
      WinOS环境下创建线程需要注意:
      (1)需要使用WinOS的API函数,要包含windows.h的头文件。
      (2)用HANDLE  handAAA对象接受新创建的线程句柄,当不在需要对先创建的线程操作时,调用CloseHandle(handAAA),将此线程关闭。注意调用此函数不是终止新创建的线程,而是表示在主线程中对新创建的线程的而引用不感兴趣,关闭线程句柄。这样新创建的线程的引用计数就会计数-1,若果新创建线程执行完毕后,系统也会递减改线程“内核对象”的使用计数。当使使用计数递减为0后,则WINOS则释放此线程内核对象 。如果主线程没有关闭句柄,则系统会易制保留一个对新创建线程的引用。这样直到“进程”执行完毕后,才能释放此线程的资源。所以在主线程中,如果不需要其他新建线程的句柄时,则可以将其及时关闭。
      (3)如果再在一个线程中使用 void Sleep(  _In_ DWORD dwMilliseconds), 则可以使本线程暂时一段时间,以毫秒为单位。
      (4)在主线程可以使用system("pause"),这样可以保证主线程不会退出,给其他线程保留足够的时间。或者也是使用Sleep函数。
      (5)多线程相互之间共享资源时,需要处理好线程之间的同步问题。在一个线程访问共享变量时,其他线程则不能访问。
        利用互斥对象实现多线程之间的共享变量。Mutex,是内核对象,保证多线程对贡献变量的互斥访问权。此对象包含使用数量,以及线程ID,以及计数器。其中线程ID表示当前拥此互斥对象的线程ID,计数器表示线程拥有虎池对象的次数。在WinOS系统中使用如下函数原型创建互斥内核对象。调用后,可以打开或者关闭一个命名或者匿名的互斥对象。调用成功将返回互斥对象的句柄。当线程对共享对象访问结束后,应该释放改对象的使用权。通过ReleaseMutex(Handle),如果释放成功返回非0,失败返回0.互斥对象的处理原则:谁拥有,谁释放。因为互斥对象中被WinOS设置了与之相关的线程ID,如果释放不是被设置ID,则不能被其他线程操作。
        当线程第一次拥有互斥对象时(1)设置被线程ID给互斥对象,(2)设置互斥对象当前线程使用(未释放)互斥对象的次数+1。
        如果请求的线程ID与互斥对象中的线程ID相等,则可以继续请求互斥对象,即使本线程此前没有释放互斥对象,此时的影响是互斥对象中的使用次数+1。每调用一次释放releaseMutex,互斥对象内部的引用-1。直到释放为0,则其他线程可以请求到此Mutex对象。
        线程需要通过主动请求共享对象的使用权才有可能获取共享对象拥有权。使用DWord WaitForSingleObject(handler, dwMilliseconds);参数hHandler获取请求对象的句柄。即互斥对象的句柄hMutex, 一旦该对象有信号,则此线程就通过此函数获取此互斥对象。否则,此线程一直停留在此函数的位置。则暂停线程。参数2,指定等待的时间间隔,毫秒单位,如果超过此事件,则此函数将返回。或者0,立即返回,或者INFINITE,无限等待下去。
        调用WaitForSingleObject后此线程一直等待下去,党(1)指定的对象为有信号,则返回,(2)超过设定的等待时间。此函数会返回(1)WAIT_OBJECT_0:请求对象有信号;(2) WAIT_TIMEOUT:超过等待时间,且请求对象信号; WAIT_ABANDONED:请求对象为互斥对象,此前的线程在执行完毕没有释放此对象,则此时当前线程获取请求对象有有权,且设定互斥对象为无信号状态。
    HANDLE WINAPI CreateMutex(
      _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,  如果为NULL,则使用默认的安全性
      _In_     BOOL                  bInitialOwner,  即确定互斥对象的初始拥有者,否则此线程将不能后的所创建的互斥对象的所有权。True,则本线程初始拥有此互斥对象,如果本线程不释放,则其他线程不能等待拥有。
      _In_opt_ LPCTSTR               lpName  指定互斥对象的名称,如果为NULL,则是匿名互斥对象。
    );
     1 // ConsoleTest.cpp : 定义控制台应用程序的入口点。
     2 //
     3 #include "stdafx.h"
     4 #include <iostream>
     5 #include <Windows.h>
     6 using namespace std;
     7 int tick = 100;
     8 HANDLE hMutex = NULL;
     9 DWORD WINAPI funName1(LPVOID para)
    10 {
    11     while (true)
    12     {
    13         WaitForSingleObject(hMutex, INFINITE);
    14         if (tick > 0)
    15             cout << "
     threadAAAAAA sell 1 tick " << tick-- << endl;
    16         else
    17             break;
    18         ReleaseMutex(hMutex);
    19     }
    20     return 0;
    21 }
    22 DWORD WINAPI funName2(LPVOID para)
    23 {
    24     while (true)
    25     {
    26         WaitForSingleObject(hMutex, INFINITE);
    27         if (tick > 0)
    28             cout << "
     threadBBBBB sell 1 tick " << tick-- << endl;
    29         else
    30             break;
    31         ReleaseMutex(hMutex);
    32     }
    33     return 0;
    34 }
    35 
    36 int _tmain(int argc, _TCHAR* argv[])
    37 {
    38     HANDLE handle1 = nullptr;
    39     HANDLE handle2 = nullptr;
    40     DWORD threadID1 = NULL;
    41     DWORD threadID2 = NULL;
    42     handle1 = CreateThread(NULL, 0, funName1, NULL, 0, &threadID1);
    43     handle2 = CreateThread(NULL, 0, funName2, NULL, 0, &threadID2);
    44 
    45     cout << "main Thread is running..." << handle1 << threadID1 << endl;
    46     cout << "main Thread is running..." << handle2 << threadID2 << endl;
    47     cout << "--------------------" << endl;
    48     CloseHandle(handle1);
    49     CloseHandle(handle2);
    50     cout << "main Thread is running..." << handle1 << threadID1 << endl;
    51     cout << "main Thread is running..." << handle2 << threadID2 << endl;
    52     hMutex = CreateMutex(NULL, false, _T("AAAA"));
    53     //
    54     system("pause");
    55     return 0;
    56 }
      在C++中的NULL,即为0,定义如下:
    #ifdef __cplusplus
    #define NULL    0
    #else  /* __cplusplus */

      

  • 相关阅读:
    《剑指Offer》题目:跳台阶
    《剑指Offer》题目:变态跳台阶
    《剑指Offer》题目:二叉树的镜像
    《剑指Offer》题目:树的子结构
    《剑指Offer》题目:合并两个排序的链表
    《剑指Offer》题目:链表中倒数第k个结点
    《剑指Offer》题目:调整数组顺序使奇数位于偶数前面
    Nginx配置http强制跳转到https
    并查集详解(转)
    AKOJ -- 1529 -- 寻找最大数
  • 原文地址:https://www.cnblogs.com/icmzn/p/5726083.html
Copyright © 2011-2022 走看看