问题中的代码给了我一些关于如何执行复制的不确定性,但我做了以下假设:
-
((N - N % 8) / 8) 是指每个父代产生多少个子cell,N,当它达到一半的生命周期时,这不是什么有问题的代码暗示
- 从创建之时开始,子细胞的生存时间与其父细胞相同 - 因此它们的寿命比父细胞长,而不是同时死亡,这又不是相关代码的作用
我用来完成概述的模拟的方案是有一个线程来控制time 变量,可以是主线程,也可以是专门为此目的创建的线程。该线程将根据需要增加时间,但将等待所有线程检查它们的单元是否已死亡或需要在增量之间重现并执行必要的操作。下面的示例演示了这种方法。
我发现当使用std::atomic 变量来存储活细胞数量、模拟时间、需要检查的线程数等时,这会更容易,也可能更清晰。使用原子变量时,为任何增量或减量执行必要的内存隔离,而不需要 std::mutex 或其他显式同步。此外,最好为细胞实现class,这样它们就可以存储自己的生命周期,如果它们还活着,无论是孩子还是父母,是否有孩子等等。
示例
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
#include <mutex>
class Cell {
public:
Cell(int x, bool child = false) {
lifetime = (0.1 + x % 8);
n = x;
is_child = child;
alive = true;
has_children = false;
}
int lifetime;
int n;
bool is_child;
bool has_children;
bool alive;
};
std::mutex mtx; // This will be used to synchronize threads.push_back()
// when creating children cells
std::vector<Cell> cells;
std::vector<std::thread> threads;
std::atomic<int> t; // The simulation time
std::atomic<int> living; // The number of living cells
std::atomic<int> check; // This will be used to ensure every thread goes through the
// necessary checks every time step
void thread_function(Cell cell) {
int prev = t;
while (living > 0) {
while (prev == t) {if (living == 0) return;}
prev = (int)t;
if (!cell.has_children && !cell.is_child && t > cell.lifetime / 2.0) {
cell.has_children = true;
// Create children and send them to new threads
for (int ii = 0; ii < ((cell.n - cell.n % 8) / 8); ii ++) {
living ++;
Cell c(ii, true); // Create a new cell which will die
c.lifetime = cell.lifetime + t; // {lifetime} seconds from now
mtx.lock();
threads.push_back(std::thread(thread_function, c));
mtx.unlock();
}
}
if (cell.alive && t >= cell.lifetime) {
cell.alive = false;
living --;
}
check --;
}
}
int main(int argn, char** argv) {
living = argn - 1;
if (argn > 1) {
for (int ii = 1; ii < argn; ii ++) {
cells.push_back(Cell(atoi(argv[ii])));
threads.push_back(std::thread(thread_function, cells[ii-1]));
}
}
t = 0;
while (living > 0) {
std::cout << "Total Cells: "+std::to_string(living)+" [ "+std::to_string(t)+
" s ]\n" << std::flush;
check = threads.size();
t ++;
while (check > 0) {
if (living == 0) break;
}
}
std::cout << "Total Cells: "+std::to_string(living)+" [ "+std::to_string(t)+
" s ]\n" << std::flush;
for (int ii = 0; ii < threads.size(); ii ++) {
threads[ii].join();
}
}
./cells 1 2 3 4 5 6 7
Total Cells: 7 [ 0 s ]
Total Cells: 6 [ 1 s ]
Total Cells: 5 [ 2 s ]
Total Cells: 4 [ 3 s ]
Total Cells: 3 [ 4 s ]
Total Cells: 2 [ 5 s ]
Total Cells: 1 [ 6 s ]
Total Cells: 0 [ 7 s ]
./cells 21 12 6 7 1 17 25
Total Cells: 7 [ 0 s ]
Total Cells: 9 [ 1 s ]
Total Cells: 4 [ 2 s ]
Total Cells: 7 [ 3 s ]
Total Cells: 6 [ 4 s ]
Total Cells: 5 [ 5 s ]
Total Cells: 4 [ 6 s ]
Total Cells: 2 [ 7 s ]
Total Cells: 0 [ 8 s ]
您可以通过包围check、t 和living 的每个增量和减量使用互斥锁来获得相同的结果。
注意 像我这样使用全局变量并不是一个好习惯,我这样做只是为了简化多线程的演示,实际上最好将它们包装在namespace 中,将整个模拟重构为class,或类似的东西。