void mt_fillNtupleFromMultipleThreads() { // Avoid unnecessary output gROOT->SetBatch(); // Make ROOT thread-safe ROOT::EnableThreadSafety(); // Total number of events const size_t nEntries = 65535; // Match number of threads to what the hardware can do const size_t nWorkers = 4; // Split work in equal parts const size_t nEventsPerWorker = nEntries / nWorkers; // Create the TBufferMerger: this class orchestrates the parallel writing auto fileName = "mt103_fillNtupleFromMultipleThreads.root"; ROOT::TBufferMerger merger(fileName); // Define what each worker will do // We obtain from a merger a TBufferMergerFile, which is nothing more than // a file which is held in memory and that flushes to the TBufferMerger its // content. auto work_function = [&](int seed) { auto f = merger.GetFile(); TNtuple ntrand("ntrand", "Random Numbers", "r"); TRandom rnd(seed); for (auto i : ROOT::TSeqI(nEntries)) ntrand.Fill(rnd.Gaus()); f->Write(); }; // Create worker threads std::vector workers; for (auto i : ROOT::TSeqI(nWorkers)) workers.emplace_back(work_function, i + 1); // seed==0 means random seed :) // Make sure workers are done for (auto &&worker : workers) worker.join(); }