1. 程式人生 > >四個執行緒寫四個檔案02 Semaphore = Event * 4

四個執行緒寫四個檔案02 Semaphore = Event * 4

但是我有一個擔心,當舉旗時,4個執行緒至少有一個執行緒不在等待該旗子時【因為執行緒排程不可預知】,這樣某個執行緒在某次命令中會不會多跑一圈?而那個掉隊的執行緒就跑不了,導致死鎖。

試驗:把threadfunc改成下面的程式碼,即讓SetEvent和WaitForSingleObject有個不確定的間隔。跑了七八下,結果都正確。但是再跑一下,死鎖真的發生了。所以這個現象是比較難以重複的。  

UINT WINAPI WriteNumberIntoFile(PVOID pParam){
	//由於建立執行緒是要一定的開銷的,所以新執行緒並不能第一時間執行到這來    
    int iNumber = *(int*)pParam+1;
    SetEvent(hEventPtrParam);//觸發事件  
	int iFileId = (iNumber+3)%iThreads;
	while(iCurrTimes < iMaxTimes){
		//通知主執行緒準備就緒
		SetEvent(hEvents[iNumber-1]);
		 Sleep(rand()%100);
		//等待主執行緒允許寫
		WaitForSingleObject(hSemaphore, INFINITE);
		//如果條件不滿足,不進行寫操作,直接跳出迴圈
		if(iCurrTimes >= iMaxTimes)			break;
		ofs[iFileId] << iNumber << " ";
		iFileId = (iFileId+3)%iThreads;
	}
	cout << "ChildThread[" << iNumber << "] Exits" << endl;
	return 0;
}

第三次嘗試: 把Semaphore改成4個自動重置事件(Event)。打個比方,四個車道,每個車道一個車(執行緒),每個車道有自己的紅綠燈(自動重置事件),燈變綠,車子跑起來,並且燈立即變紅。這樣不會出現某個車跑2圈,而另外的某車跑不了的情況。 嘗試也成功了。但我認為這個可靠多了。缺點是多用了一些同步物件。但正確性總是第一位的。  

#include <Windows.h>
#include <shlwapi.h> 
#include <strsafe.h>
#include<cstdlib>
#include<ctime>
#include<string>
#include<iostream>
#include <fstream>
#include <process.h>
using namespace std;
 
#pragma comment(lib,"Shlwapi.lib")
 
 
const int iThreads = 4;//執行緒個數
//HANDLE hSemaphoreNotifyChilds;//主執行緒通知子執行緒的“交通訊號燈”
HANDLE hEventNotifyChilds[iThreads];//主執行緒通知子執行緒開始寫 
HANDLE hEventPtrParam;//用於主執行緒和子執行緒之間的同步(傳遞i的值)
HANDLE hEventNotifyMain[iThreads] = {0};//自動置位事件,用於子執行緒通知主執行緒
ofstream ofs[iThreads];//四個輸出檔案流
 
int iMaxTimes = 10;//最大輸出次數
int iCurrTimes = 0;//當前輸出次數
 
UINT WINAPI WriteNumberIntoFile(PVOID pParam){
	//由於建立執行緒是要一定的開銷的,所以新執行緒並不能第一時間執行到這來    
	int iNumber = *(int*)pParam+1;
	SetEvent(hEventPtrParam);//觸發事件  
	int iFileId = (iNumber+3)%iThreads;
	while(iCurrTimes < iMaxTimes){
		//通知主執行緒準備就緒
		SetEvent(hEventNotifyMain[iNumber-1]);
		Sleep(rand()%100);//【因為執行緒排程不可預知】
		//等待主執行緒允許寫
		//WaitForSingleObject(hSemaphoreNotifyChilds, INFINITE);
		WaitForSingleObject(hEventNotifyChilds[iNumber-1], INFINITE);
		//如果條件不滿足,不進行寫操作,直接跳出迴圈
		if(iCurrTimes >= iMaxTimes)			break;
		ofs[iFileId] << iNumber << " ";
		iFileId = (iFileId+3)%iThreads;
	}
	cout << "ChildThread[" << iNumber << "] Exits" << endl;
	return 0;
}
 
void main()
{
	TCHAR szExe[MAX_PATH]; memset(szExe, 0, sizeof(TCHAR)*MAX_PATH);
	GetModuleFileName(NULL, szExe, MAX_PATH);
 
	TCHAR szCurrWorkPath[MAX_PATH]; memset(szCurrWorkPath, 0, sizeof(TCHAR)*MAX_PATH);
	GetCurrentDirectory(MAX_PATH, szCurrWorkPath);
 
 
	TCHAR szFile[MAX_PATH]; TCHAR szSpar[] = TEXT("\\");
	const TCHAR* szFiles[iThreads] = {TEXT("A.txt"), TEXT("B.txt"), TEXT("C.txt"), TEXT("D.txt")};
	for(int i = 0; i < iThreads; i++){//如果當前檔案存在,則刪除;
		memset(szFile, 0, sizeof(TCHAR)*MAX_PATH);
		StringCchCopy(szFile, lstrlen(szCurrWorkPath)+1, szCurrWorkPath); //D:/Projects/BoostPro/BoostPro
		StringCchCat(szFile, lstrlen(szFile)+lstrlen(szSpar)+1, szSpar);//D:/Projects/BoostPro/BoostPro/
		StringCchCat(szFile, lstrlen(szFile)+lstrlen(szSpar)+lstrlen(szFiles[i])+1, szFiles[i]);//D:/Projects/BoostPro/BoostPro/X.txt
		if(PathFileExists(szFile) && !DeleteFile(szFile))		MessageBox(NULL, szFile, TEXT("Msg"), MB_OK);
	}
 
 
	srand((unsigned)time(NULL));//設定隨機數的種子
 
 
	//hSemaphoreNotifyChilds = CreateSemaphore(NULL, 0, iThreads, NULL);
	//初始化事件 自動置位,初始無觸發
	hEventPtrParam = CreateEventW(NULL, FALSE, FALSE, NULL); 
	HANDLE hThreads[iThreads]; memset(hThreads, 0, sizeof(HANDLE)*4);
	for(int i = 0; i < iThreads; i++){//等子執行緒接收到引數時主執行緒可能改變了這個i的值 
		//建立事件,自動置位,初始復位
		hEventNotifyChilds[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
		hEventNotifyMain[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
		//開啟檔案
		ofs[i].open(szFiles[i], ios_base::trunc);	
		//建立執行緒
		unsigned uThreadId;
		hThreads[i] = (HANDLE)_beginthreadex(NULL, 0, &WriteNumberIntoFile, (LPVOID)(&i), 0, &uThreadId);
		WaitForSingleObject(hEventPtrParam, INFINITE);//等待事件被觸發,等待到復位後,立馬置位
	}
 
	for(; iCurrTimes < iMaxTimes; ++iCurrTimes){
		cout << "Main thread is waiting..." << endl;
		//等待就緒子執行緒的通知
		WaitForMultipleObjects(iThreads, hEventNotifyMain, TRUE, INFINITE);
		//通知就緒子執行緒進行寫操作
		//ReleaseSemaphore(hSemaphoreNotifyChilds, iThreads, NULL);
		for(int i = 0; i < iThreads; i++)			SetEvent(hEventNotifyChilds[i]);
		cout << "iCurrTimes = " << iCurrTimes << endl;
	}
	//保證子執行緒已全部執行結束  
	cout << "WaitFor ChildThreads, iCurrTimes = " << iCurrTimes << endl;
	//主執行緒將iCurrTimes+1變為10之前,子執行緒可能已經陷入了等待
	//ReleaseSemaphore(hSemaphoreNotifyChilds, iThreads, NULL);
	for(int i = 0; i < iThreads; i++)			SetEvent(hEventNotifyChilds[i]);
	WaitForMultipleObjects(iThreads, hThreads, TRUE, INFINITE);  
 
	//CloseHandle(hSemaphoreNotifyChilds);
	for(int i = 0; i < iThreads; i++){
		//銷燬事件
		CloseHandle(hEventNotifyChilds[i]);
		CloseHandle(hEventNotifyMain[i]);
		//關閉執行緒控制代碼
		CloseHandle(hThreads[i]);
		//關閉檔案
		ofs[i].close();
	}
}