如题 如何在多线程中手动进行数据合并
缘起
笔者不喜欢ROOT,也不太会用。对于小规模的数据,使用CSV存储查看起来更为方便。但是在Geant4中使用csv存储似乎不支持数据的自动合并。也就是说在面对多线程任务时,输出数据是一个困难。
而且,如果在RunAction或者main()中进行文件读写,会出现多个线程同时读写同一文件的情况,如果没有加文件锁,很容易报错。
问题的关键
笔者发现Geant4的每一个线程都会执行一遍main(){}以及RunAction(){}中的内容,这会给多线程数据手动合并带来困扰。因为难以判断当前是哪一个线程在执行什么,并且在上述地方使用GetThreadID()也并不能正确获取ID。
问题的钥匙
Geant4中,线程分为1个主线程和若干个工作线程。在RunAction中的内容,先由工作线程执行一遍,随即由主线程再执行一遍。因此获取ThreadID的结果总会被主线程的ThreadID=-1说覆盖。
在Geant4.10版本,可以在RunAction中使用 IsMaster() 来判断是否在主线程,因此这也是解决手动数据合并的钥匙。
笔者的做法
在RunAction类中创建静态变量,在RunAction.cc的RunAction::EndOfRunAction(const G4Run* run)使用IsMaster判断是否为主线程,并在工作线程的地方累加静态变量,在Master中进行数据输出和文件读写。
大概逻辑如下:
class RunAction : public G4UserRunAction
{
public:
RunAction();
virtual ~RunAction();
virtual void BeginOfRunAction(const G4Run*);
virtual void EndOfRunAction(const G4Run*);
private:
G4String OutputFileName;
static std::vector<G4double> TotalEdepInSensors;
};
std::vector<G4double> RunAction::TotalEdepInSensors(0);
RunAction::SFFSRunAction(){
...
}
...
void RunAction::EndOfRunAction(const G4Run* run)
{
if(IsMaster()){
std::ofstream results_file;
results_file.open("data.csv");
results_file << TotalEdepInSensors[1] << std::endl;
results_file.close();
std::fill(this->TotalEdepInSensors.begin(), this->TotalEdepInSensors.end(), 0);
}
else{
TotalEdepInSensors[1] += Edep;
}
|