編程思想之多線程與多進程(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——性能分析