在多線程程式設計中,共享資源的存取同步是核心課題。本文透過具體測試案例,展示臨界區在保護檔案寫入操作時的同步效果,並分析同步與非同步模式下的資料完整性差異與效能代價。
class CMyFile {
public:
CMyFile() { InitializeCriticalSection(&cs); }
~CMyFile() { DeleteCriticalSection(&cs); }
void MsgLog(char* buffer, int length, int wait) {
static int i = 0;
CString seq;
seq.Format("%d#", i++);
if (m_bSync) EnterCriticalSection(&cs);
fwrite("\n", 1, 1, fp);
fwrite(seq + CString('-', 50), 52, 1, fp);
fwrite("\n", 1, 1, fp);
fwrite(buffer, length, 1, fp);
Sleep(wait); // 模擬處理延遲
fwrite("\n", 1, 1, fp);
fwrite(seq + CString('-', 50), 52, 1, fp);
fflush(fp);
if (m_bSync) LeaveCriticalSection(&cs);
}
bool OpenFile(CString filename, bool bSync) {
m_bSync = bSync;
fp = fopen(filename, "wt");
return fp != NULL;
}
void CloseFile() { if (fp) fclose(fp); }
protected:
CRITICAL_SECTION cs;
FILE* fp = nullptr;
CString m_filename;
bool m_bSync = false;
};
UINT ThreadFunc(LPVOID p) {
CMyFile* file = (CMyFile*)p;
long tid = AfxGetThread()->m_nThreadID;
CString prefix;
prefix.Format("thread %d", tid);
for (int i = 0; i < 100; i++) {
CTime now = CTime::GetCurrentTime();
CString log = now.Format("%y-%m-%d %H:%M:%S") + "--------->" + prefix;
file->MsgLog(log.GetBuffer(), log.GetLength(), tid / 100);
}
return 1;
}
void CBDlg::OnButtonTest() {
CMyFile file;
file.OpenFile("1.txt", m_sync.GetCheck()); // 由 UI 決定是否同步
CWinThread* threads[10];
HANDLE handles[10];
for (int i = 0; i < 10; i++) {
threads[i] = AfxBeginThread(ThreadFunc, &file);
Sleep(10); // 錯開啟動時間
handles[i] = threads[i]->m_hThread;
}
WaitForMultipleObjects(10, handles, TRUE, INFINITE);
file.CloseFile();
AfxMessageBox("ok");
}
| 模式 | 資料完整性 | 輸出順序 | 執行時間 |
|---|---|---|---|
| 無同步 | 虛線配對錯亂、內容交疊 | 亂序 | 較快 |
| 臨界區同步 | 每筆記錄虛線完美配對 | 依線程取得鎖順序排列 | 顯著增加 |
CSingleLock/CMultiLock 管理鎖生命週期,避免例外導致死鎖SRWLOCK (Vista+) 或無鎖佇列 以提升高併發效能