退出的时候,主线程在iCurr+1之后再唤醒各个子线程。这样做的好处是:防止子线程在主线程改变iCurr的值之前检测循环退出的条件,然后陷入无限的等待中。
#include<ctime>
#include<cstdio>
#include<iostream>
#include <fstream>
using namespace std;
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio.hpp>
using namespace boost;
typedef boost::mutex CMutex;
CMutex oMutexConsole;
const int iThreadCnt = 4;//线程的个数
bool bChildIsWriting[iThreadCnt] = {false, false, false, false};
/*
子线程写数字到文件之前:bChilds[i] = false;
子线程写数字到文件的过程中:bChilds[i] = true;
子线程写数字到文件之后:bChilds[i] = false;
*/
boost::mutex muForLock;//互斥量
boost::condition_variable cvMain;//主线程通知子线程的条件变量
std::ofstream oFiles[iThreadCnt];//四个输出文件流
const int iTimes = 10;//每个线程输出数字序列的长度
int iCurr = 0;//当前输出数字的个数
//可变参数列表函数
void ConsoleOutput(bool bClient, char* lpszFmt, ...){
//主线程输出:FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
//客户端线程输出:FOREGROUND_RED
//服务器线程输出:FOREGROUND_GREEN
va_list pArgLst;
va_start(pArgLst, lpszFmt);
oMutexConsole.lock();
if(bClient) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED);
else SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);
vfprintf(stdout, lpszFmt, pArgLst);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
oMutexConsole.unlock();
va_end(pArgLst);
}
void WriteNumberIntoTxt(const int writedata){
int iFileNo = writedata;//每个线程要写的数字【始终如一】
while(iCurr < iTimes){
mutex::scoped_lock oLock(muForLock);
//初始的时候,条件不满足,所有子线程都陷入了等待中
while(!bChildIsWriting[writedata-1]){cvMain.wait(oLock);}
this_thread::sleep(posix_time::seconds(rand()%2)); //模拟实际运行中可能遇到的阻塞
iFileNo = (iFileNo+3)%iThreadCnt; //确定要写的文件【每个线程都不一样】
oFiles[iFileNo] << writedata << " "; //开始写
bChildIsWriting[writedata-1] = false; //为下一轮一起写文件做好准备
}
ConsoleOutput(true, "Thread%d Exits\n", writedata);
}
void main()
{
const char* lpszFileName[iThreadCnt] = {"A.txt", "B.txt", "C.txt", "D.txt"};
boost::thread oThreads[iThreadCnt];
for(int i = 0; i < iThreadCnt; i++){
//打开文件【如果不存在则创建】
oFiles[i].open(lpszFileName[i], std::ios_base::trunc);
//创建线程【传递的参数为线程要写的数字】
oThreads[i] = boost::thread(WriteNumberIntoTxt, i+1);
}
srand((unsigned)time(NULL));//设置随机数的种子
for(; iCurr < iTimes;){
ConsoleOutput(false, "MainThread is Waiting...\n");
//只要还有一个子线程没写完文件,就一直等下去
while(bChildIsWriting[0] || bChildIsWriting[1] || bChildIsWriting[2] || bChildIsWriting[3]){boost::this_thread::yield();}
{
mutex::scoped_lock oLock(muForLock);
//让所有的子线程开始写
for(int i = 0; i < iThreadCnt; i++) bChildIsWriting[i] = true;
//子线程可能在检测到退出条件(iCurr >= iTimes)之前陷入阻塞,无限等待
iCurr++;//让每个子线程都满足退出的条件再唤醒
cvMain.notify_all(); //唤醒所有被阻塞的子线程
}
ConsoleOutput(false, "This round is %d.\n", iCurr);
}
//等待线程执行结束
for(int i = 0; i < iThreadCnt; i++) oThreads[i].join();
//关闭所有文件
for(int i = 0; i < iThreadCnt; i++) oFiles[i].close();
}