题目:
有四个线程1、2、3、4。线程1的功能就是输出1,线程2的功能就是输出2,以此类推.........现在有四个文件ABCD。初始都为空。现要让四个文件呈如下格式:A:1 2 3 4 1 2....B:2 3 4 1 2 3....C:3 4 1 2 3 4....D:4 1 2 3 4 1....请设计程序。
解答:
我的思路是这样的:
当每个子线程准备就绪后,主线程发出前进一步的命令【举旗】。这时每个子线程都收到该命令,找到正确的文件,打印自己的编号。然后向主线程报告自己已经完成任务,准备接受主线程的下一个命令。
这里的要点是每个子线程收到命令【举旗】后,该命令必须结束【放旗】。否则子线程完成该命令后会继续执行该命令。
第一次尝试:
使用自动重置事件(Event)作为主线程的旗子,举旗后,等待的四个线程跑起来。
但是这种方案不行,因为任何一个线程跑起来之后,旗子就放下了,导致其他线程跑不起来。
如果使用手工重置事件作为主线程的旗子,又找不到合适的机会放下旗子。
第二次尝试:
把Event改成Semaphore,让4个等待线程可以跑。尝试成功。
#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 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]); //等待主线程允许写 WaitForSingleObject(hSemaphoreNotifyChilds, 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的值 //创建事件,自动置位,初始复位 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); cout << "iCurrTimes = " << iCurrTimes << endl; } //保证子线程已全部运行结束 cout << "WaitFor ChildThreads, iCurrTimes = " << iCurrTimes << endl; //主线程将iCurrTimes+1变为10之前,子线程可能已经陷入了等待 ReleaseSemaphore(hSemaphoreNotifyChilds, iThreads, NULL); WaitForMultipleObjects(iThreads, hThreads, TRUE, INFINITE); CloseHandle(hSemaphoreNotifyChilds); for(int i = 0; i < iThreads; i++){ //销毁事件 CloseHandle(hEventNotifyMain[i]); //关闭线程句柄 CloseHandle(hThreads[i]); //关闭文件 ofs[i].close(); } }