帶你玩轉Visual Studio——VC++的多線程開發

編程思想之多線程與多進程(4)——C++中的多線程一文中講了VC++的多線程的用法和用例,本文接着這個話題作進一步的講解。如果你是初次接觸C++多線程或想對多線程與多進程的基礎有進一步瞭解,可查看以下文章:《編程思想之多線程與多進程(1)——以操作系統的角度述說線程與進程》、《編程思想之多線程與多進程(2)——線程優先級與線程安全》、《編程思想之多線程與多進程(4)——C++中的多線程


SuspendThread和ResumeThread

編程思想之多線程與多進程(1)——以操作系統的角度述說線程與進程一文中我們知道:操作系統的線程有幾種狀態的變化:執行(運行),掛起(阻塞)和恢復(就緒)執行。
當線程做完任務或者現在想暫停線程運行,就需要使用SuspendThread來暫停線程的執行,當然恢復線程的執行就是使用ResumeThread函數了。這兩個函數使用很簡單的,下面就來看看例子是怎麼樣使用的。

函數原型如下:
掛起線程

DWORD WINAPI SuspendThread(_In_ HANDLE hThread);

恢復線程

DWORD WINAPI ResumeThread(_In_ HANDLE hThread);

說明:hThread爲指定線程的句柄。

繼續編程思想之多線程與多進程(4)——C++中的多線程一文中的同步線程代碼:

#define NAME_LINE   40

//定義線程函數傳入參數的結構體
typedef struct __THREAD_DATA
{
    int nMaxNum;
    char strThreadName[NAME_LINE];

    __THREAD_DATA() : nMaxNum(0)
    {
        memset(strThreadName, 0, NAME_LINE * sizeof(char));
    }
}THREAD_DATA;

HANDLE g_hMutex = NULL;     //互斥量

                            //線程函數
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
    THREAD_DATA* pThreadData = (THREAD_DATA*)lpParameter;

    for (int i = 0; i < pThreadData->nMaxNum; ++i)
    {
        //請求獲得一個互斥量鎖
        WaitForSingleObject(g_hMutex, INFINITE);
        std::cout << pThreadData->strThreadName << " --- " << i << std::endl;
        Sleep(100);
        //釋放互斥量鎖
        ReleaseMutex(g_hMutex);
    }
    return 0L;
}

我們改一下測試代碼,如下:

void Test()
{
    //創建一個互斥量
    g_hMutex = CreateMutex(NULL, FALSE, NULL);

    //初始化線程數據
    THREAD_DATA threadData1, threadData2;
    threadData1.nMaxNum = 5;
    strcpy_s(threadData1.strThreadName, "線程1");
    threadData2.nMaxNum = 10;
    strcpy_s(threadData2.strThreadName, "線程2");


    //創建第一個子線程
    HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc, &threadData1, 0, NULL);
    //創建第二個子線程
    HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc, &threadData2, 0, NULL);

    // 掛起線程
    SuspendThread(hThread1);                    // [代碼段1]

    //Sleep(500);                                   // [代碼段2]
    //ResumeThread(hThread1);                       // [代碼段2]

    //關閉線程
    CloseHandle(hThread1);
    CloseHandle(hThread2);

    system("pause");
}

當我們打開[代碼段1],註釋[代碼段2]時,結果如下:

線程2 — 0
線程2 — 1
線程2 — 2
線程2 — 3
線程2 — 4
線程2 — 5
線程2 — 6
線程2 — 7
線程2 — 8
線程2 — 9

可以發現線程1被掛起,並沒有執行。


當我們同時打開[代碼段1]和[代碼段2],結果如下:

線程2 — 0
線程2 — 1
線程2 — 2
線程2 — 3
線程2 — 4
線程1 — 0
線程2 — 5
線程1 — 1
線程2 — 6
線程1 — 2
線程2 — 7
線程1 — 3
線程2 — 8
線程1 — 4
線程2 — 9

可以發現線程1在0.5秒之後纔開始執行,這是因爲線程1在掛起0.5秒之後才被喚醒(恢復),開始執行。



線程與同步鎖的封裝類

封裝類源代碼

#ifndef CTHREAD_H_

#include <windows.h>

// 封裝的線程類
class CThread 
{
public:
    CThread()
        : m_bStopped(false)
    {
        m_hThread = CreateThread(NULL, 0, StartRoutine, this, CREATE_SUSPENDED, &m_nId);
    }

    virtual ~CThread()
    {
        if (m_hThread) {
            CloseHandle(m_hThread);
        }
    }

protected:
    // 執行函數,子類應該實現這個方法,否則線程什麼也不做
    virtual void Run()
    {
    }

public:
    // 開始執行線程
    virtual void Start()
    {
        ResumeThread(m_hThread);
    }

    // 線程是否停止
    bool Stopped()
    {
        return m_bStopped;
    }


    void Join()
    {
        if (m_hThread) {
            WaitForSingleObject(m_hThread, INFINITE);
        }
    }

private:
    // 線程執行的起始地址,也叫線程函數
    static DWORD WINAPI StartRoutine(LPVOID param)
    {
        CThread * thread = (CThread*)param;
        thread->Run();
        thread->m_bStopped = true;
        return 0;
    }

private:
    HANDLE          m_hThread;      // 線程句柄
    bool            m_bStopped;     // 線程是否停止
    DWORD           m_nId;          // 線程ID
};

// 封裝的互斥量類
class CMutex 
{

public:
    CMutex()
    {
        // 創建互斥量鎖
        m_hMutex = CreateMutex(NULL, FALSE, NULL);
    }
    ~CMutex()
    {
        // 釋放互斥量鎖
        if (m_hMutex)
            CloseHandle(m_hMutex);
    }

public:
    // 加鎖,獲取互斥量鎖,鎖定資源
    bool Lock()
    {
        if (m_hMutex) 
        {
            return WaitForSingleObject(m_hMutex, INFINITE) == WAIT_OBJECT_0;
        }
        return false;
    }

    // 試圖鎖定資源,判斷當前的互斥量是否被佔用。
    // 返回true說明該鎖爲非佔用狀態,可獲得該鎖;返回false說明該鎖爲佔用狀態,需等待被釋放
    bool TryLock()
    {
        if (m_hMutex) {
            return WaitForSingleObject(m_hMutex, 0) == WAIT_OBJECT_0;
        }
        return false;
    }

    // 解鎖,釋放互斥量鎖
    void Unlock()
    {
        if (m_hMutex)
            ReleaseMutex(m_hMutex);
    }

private:
    HANDLE          m_hMutex;       // 互斥量句柄
};

// 互斥量鎖的抽象
// 只要聲明該對象即鎖定資源,當退出其(該對象)作用域時即釋放鎖
class CLock 
{
public:
    CLock(CMutex &mutex)
        : m_mutex(mutex)
    {
        m_bLocked = m_mutex.Lock();
    }

    ~CLock()
    {
        if (m_bLocked)
            m_mutex.Unlock();
    }

private:
    // 禁用賦值操作符
    CLock & operator = (CLock&)
    {
        return *this;
    }

private:
    CMutex&         m_mutex;        // 互斥量句柄的引用
    bool            m_bLocked;      // 互斥量是否被鎖定(佔用)
};

#endif  // CTHREAD_H_

測試程序

#include <iostream>
#include <string>
#include "CThread.h"

CMutex g_metux;

class TestThread : public CThread
{
public:
    TestThread(const std::string& strName)
        : m_strThreadName(strName)
    {
    }

    ~TestThread()
    {
    }

public:
    virtual void Run()
    {
        for (int i = 0; i < 50; i ++)
        {
            CLock lock(g_metux);
            std::cout << m_strThreadName << ":" << i << std::endl;
            //Sleep(100);
        }
    }
private:
    std::string m_strThreadName;
};


int main()
{
    TestThread thread1("Thread1");
    thread1.Start();
    TestThread thread2("Thread2");
    thread2.Start();

    system("pause");
    return 0;
}

結果

Thread1:1
Thread2:1
Thread1:2
Thread2:2
Thread1:3
Thread2:3
Thread1:4
Thread2:4
Thread1:5
Thread2:5
Thread1:6
Thread2:6
Thread1:7
Thread2:7
Thread1:8
Thread2:8
Thread1:9
Thread2:9
Thread1:10
Thread2:10
Thread1:11
Thread2:11
……



多線程調試

選擇”Debug->Windows->Threads”菜單調出線程監視窗口。在這裏你能看到程序中的所有線程,打斷點單步調試,你會看到執行路徑在線程與線程之間切換。


多線程調試
多線程調試



上一篇回顧:
帶你玩轉Visual Studio——VS2015的新功能和特性

下一篇要講述的內容:
帶你玩轉Visual Studio——性能分析


發佈了217 篇原創文章 · 獲贊 2478 · 訪問量 501萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章