1. 程式人生 > >分享一個事務處理執行緒類

分享一個事務處理執行緒類

 Windows下建立執行緒是很簡單的,具體建立執行緒的程式碼可以用AfxBeginThread(),也可以用CreateThread(),也可以用_beginthreadex()。大家寫的恐怕手都磨出老繭了。

但是,有時候我們經常會遇到一種情況,比如說每隔一會我就要做一些事情,而且必須線上程裡做。這時候該怎麼辦呢?最簡單的辦法當然是,每次都建立一個執行緒就OK了。但是身為程式設計師,我們不能對自己的要求僅僅是程式能執行就行了。作為一種更節約資源的辦法,在程式啟動的時候建立執行緒,後面在需要做一些事情的時候,把這些事情作為事務提交給執行緒處理。

當然了,以現在計算機的執行能力來說,每次都建立一個執行緒也不是什麼問題。對於普通使用者來說,程式每秒建立10個執行緒和程式一共只建立一個執行緒,基本上根本就感覺不出差別。但是我們自己必須知道,建立一個執行緒這樣的核心物件,其實開銷還是比較大的。從另一方面來說,一共只建立一個執行緒,方便對執行緒進行狀態檢測、控制。如果程式是執行在伺服器端的話,那就更應該這樣做了,每次都建立一個執行緒絕對不是個好主意。

這裡,我提供一個基本的事務處理執行緒類的模板。

Transaction.h

#ifndef _WINDOWS_THREAD_TRANSACTION_H_
#define _WINDOWS_THREAD_TRANSACTION_H_

#pragma once

class CRunnable
{
public:
	virtual void Run(void)=0;
};

class CTransaction
{
public:
	CTransaction(void);
	~CTransaction(void);

	void Init();
	void Clear();
	void Do(CRunnable & run);

protected:
	void*		m_hThread;
	unsigned	m_ThreadID;

	static unsigned __stdcall ThreadFunc(void* pArguments);
};

#endif

Transaction.cpp
#include "Transaction.h"
#include <windows.h>
#include <process.h>

#define UM_THREAD_NOTIFY	(WM_USER + 150)

CTransaction::CTransaction(void):m_hThread(NULL),m_ThreadID(0)
{
}

CTransaction::~CTransaction(void)
{
	if (0 != m_ThreadID)
	{
		Clear();
	}

	if (NULL != m_hThread)
	{
		::CloseHandle(m_hThread);
	}
}

void CTransaction::Init()
{
	m_hThread = (void*)_beginthreadex( NULL, 0, &CTransaction::ThreadFunc, this, 0, &m_ThreadID);
}

void CTransaction::Clear()
{
	::PostThreadMessage((DWORD)m_ThreadID, WM_QUIT,0,0);
	::Sleep(50);
	
	DWORD dwExitCode = 0;
	::GetExitCodeThread(m_hThread, &dwExitCode);

	if (STILL_ACTIVE  == dwExitCode)
	{
		DWORD dw = ::WaitForSingleObject(m_hThread ,1000);
		switch (dw)
		{
		case WAIT_TIMEOUT:
			::TerminateThread(m_hThread, 1);
			break;
		default:
			;
		}
	}

	m_ThreadID = 0;	// 執行緒ID置0做為退出標誌
}

unsigned __stdcall CTransaction::ThreadFunc(void* pArguments)
{;
	CTransaction *p = (CTransaction*)pArguments;

	MSG msg;
	ZeroMemory(&msg,sizeof(MSG));
	::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);

	CRunnable * pRun = NULL;
	BOOL bRet;
	while ((bRet = ::GetMessage( &msg, 0, WM_QUIT, UM_THREAD_NOTIFY)) != 0)
	{
		if (bRet == -1)
		{
			// handle the error and possibly exit
			continue;
		}

		if (UM_THREAD_NOTIFY == msg.message)
		{
			pRun = (CRunnable*)msg.wParam;
			pRun->Run();
		}
	}

    _endthreadex(0);
    return 0;
}

void CTransaction::Do(CRunnable & run)
{
	::PostThreadMessage((DWORD)m_ThreadID, UM_THREAD_NOTIFY, (WPARAM)&run, 0);
}

這裡提供了一個執行緒事務處理的基本的模型。

具體的使用說明。對於要處理的事務,繼承自CRunnable類,實現它的Run()方法。宣告一個處理執行緒的全域性或模組物件,比如命名為m_tran。在程式或者程式介面的初始化函式中呼叫m_tran.Init();對於事務類,宣告相應的事務物件,必須是模組級物件或全域性物件,不能是區域性物件。比如說命名為m_myTran。

在要執行的時候呼叫

m_tran.Do(m_myTran);

這樣就可以了。這個模型的實現利用了windows的執行緒訊息。因為只建立了一個執行緒,所以基本不需要考慮互斥之類的問題。當然如果另外還建立了其他的執行緒,處理的事務中有操作其他執行緒也有操作的資料,那就需要考慮了。

這個模型應付一般的應用場景是沒問題的。但如果事務處理的數量非常大,連續請求,就不太合適了。因為windows執行緒訊息的響應速度並不很快。這時候相對來說效率就不很高。這種情況下就不適合用windows執行緒訊息來控制,而可以用執行緒間隔一定時間來檢查是否有事務需要處理,處理完事務後立即連續檢查,沒有新的事務則待會再檢查的方式來控制了。