Wednesday, March 25, 2009

MFC 多執行緒的鎖 - Lock

設計多執緒程式可以避免主程式執行工作時,被運算量較大的工作給絆住,造成視窗延遲的現象。但是在這樣的情況下,主程式的使用者可能會重複觸發程式中的子執行緒行程,使得同時有2個以上的相同子執行緒在工作。此外例如子執行緒工作尚未結束前,主程式的某某些特定工作時必須要等待子執緒的工作完成,程式該如何撰寫。類似的問題,可透過 Lock 來完成。

MFC 的 Lock 主要是由以下這幾項構成︰
CSemaphore mutex; // 全域變數,用來計數 lock 數
CSingleLock wait(&mutex); // 操作 lock 的類別
wait.Lock(INFINITE); // 要求鎖定 mutex
wait.Unlock(); // 要求解鎖 mutex

當程式嘗試呼叫 wait.Lock(INFINITE); 鎖定 mutex 時,若 mutex 已經在其他地方被鎖定,則程式在此處會停擺,直到其他地方解除鎖定。


#include "testview.h"
#include <>

CSemaphore mutex; // 宣告

UINT ChildThread_Function ( LPVOID param )
{
 CSingleLock wait(&mutex); // 操作 mutex 的類別
 wait.Lock(INFINITE); // 要求鎖定 mutex

 CTestView* view = (CTestView*) param;
 param->DoSomething();

 wait.Unlock(); // 要求解鎖 mutex
}

CTestView::DoSomething()
{
 ....
}

CTestView::Execute()
{
 AfxBeginThread( ChildThread_Function, (LPVOID) this, THREAD_PRIORITY_ABOVE_NORMAL); // 呼叫子執行緒
}

*************************************************************************************

若目的只要要求同一個 ChildTread 同一時間只能有一個執行,則可以使用較簡單的 Critical Section 方法。

CRITICAL_SECTION csObject; // 宣告為全域
InitialCriticalSection(&csObject); // 在主程式中初始化
EnterCriticalSection(&csObject); // 在ChildThread的開頭呼叫
LeaveCriticalSection(&csObject); // 在 ChildThread的結尾呼叫

#include "testview.h"
#include <>

CRITICAL_SECTION csObject; // 宣告

UINT ChildThread_Function ( LPVOID param )
{
 EnterCriticalSection(&csObject); // 鎖定

 CTestView* view = (CTestView*) param;
 param->DoSomething();

 LeaveCriticalSection(&csObject); // 解鎖
}

CTestView::CTestView()
{
 InitialCriticalSection(&csObject); // 初始化
}

CTestView::DoSomething()
{
 ....
}

CTestView::Execute()
{
 AfxBeginThread( ChildThread_Function, (LPVOID) this, THREAD_PRIORITY_ABOVE_NORMAL); // 呼叫子執行緒
}

簡單的 MFC 多執行緒寫法

一般程式的執行過程是一個指令一個指令逐步動作,當前一步指令尚未結束前,下一個指令就不會執行,這樣的操作是屬於單執行緒程式。
所謂多執行緒,則是程式在主要的執行緒之外,新增其他執行緒工作,使2個(或多個)執行緒同時進行工作,也稱為平行處理程式。
我的功力只算剛入門,充其量就是剛寫出一個有子執行緒的小程式,這裡做個筆記。

在 VC++ 所建立的 MFC 專案內,假設要加入子執行緒的地方在 testview.cpp 內 (類別為 CTestView,繼承自 CView)
如果要開啟子執行緒的函式為 CTestView::Execute() ,則:
#include "testview.h"

UINT ChildThread_Function ( LPVOID param )
{
CTestView* view = (CTestView*) param;
view->DoSomething();
}

CTestView::DoSomething()
{
....
}

CTestView::Execute()
{
AfxBeginThread( ChildThread_Function, (LPVOID) this,  THREAD_PRIORITY_ABOVE_NORMAL);
}
ChildThread_Function 是全域函式,函式名稱由使用者自訂
AfxBeginThread 是MFC內呼叫子執行緒的方法,第一個參數是要執行的全域函式名稱(ChildThread_Function),第2個參數則是該全域函式的傳入值,此處傳入的是 CTestView 的指標。ChildThread_Function 這個子執行緒則是會呼叫 CTestView 的成員函式 DoSomething() 執行工作。

如果 ChildThread_Function 是 CTestView 的類別成員,則必須宣告為 static 才可以︰
class CTestView
{
..
..
static UINT ChildThread_Function ( LPVOID param );
};

UINT CTestView::ChildThread_Function ( LPVOID param )
{
..
..
}

Tuesday, March 17, 2009

建立動態連結函式庫 - Visual C++ 2005

這是一個簡單的說明跟筆記,相關資料可參考網路上的文章
深入C++Builder 探訪動態連結函式庫 (Dynamic Linking Libraries,DLLs)
NaCl -動態連結函式庫(Dynamic Linking Libraries,DLLs)

在 VC2005 裡,可以建立的範本似乎僅有 MFC DLL 專案,
依據其說明,此專案也可建立非MFC應用程式使用的 DLL

1. 在新增專案裡直接選 "Visual C++" -> "MFC" -> "MFC DLL"
2. 設定 MFC 是採用共用 DLL 或靜態連結,目前僅測試共用DLL的類型
3.新增新類別,假設類別名稱是 test (專案自動產生的檔案可以暫時都不要理它)
test.h 檔的內容︰
#pragma once

#ifdef DLL_EXPORT
#define DLLACT __declspec(dllexport)
#else
#define DLLACT __declspec(dllimport)
#endif
// 宣告
extern "C" class DLLACT test
{
public:
test(void);
public:
~test(void);
void ShowDialog();
};

#undef DLLACT
test.cpp
#include "StdAfx.h"
#include "test.h"

test::test(void) { }

test::~test(void) { }

void test::ShowDialog()
{
AfxMessageBox("Hello World!!", NULL, MB_OK);
}
4. 編譯dll專案

5. 建立另外一個新專案,把上述編譯完成的 .dll, .lib, 及 test.h 檔複製到新專案目錄下

6. 在新專案的屬性設定中引用 .lib 函式庫

7. 在程式中引用 test.h ,並直接使用 test 類別

透過這樣的方式,就可以編譯好一個擁有 test 類別的 dll 檔,並可以在新專案中,以使用靜態函式庫的方式來使用 test 這個類別。

Wednesday, March 04, 2009

IT筆記與碎碎念貼文分開囉

當文章慢慢累積之後,慢慢的覺得碎碎念的一些東西,不是很適合跟資訊相關筆記放在一起。這個想法有一陣子了,但是一直沒有動手做,之前也都沒注意到有什麼好方法。

最近在修版面設定的時候,注意到Blogger有個匯入匯出的功能,原本想說這只能用來做備份,不過今天心血來潮,就試著動手匯出所有資料,然後再透過Blogger提供的工具,把已匯出的資料再匯入到新的部落格裡,最後把不想移過去的文章刪掉(透過blogger的標籤可以批次刪文),竟然也就完成了。

以後碎碎念的東西,就轉移陣地啦,這裡放一些資訊相關的筆記就好。等到一切搞定後,這裡不該出現的就會不見了,新部落格弄好後就會在這裡放個連結囉。

ps. 新部落格開張