简体   繁体   English

Conway生命游戏的多线程Java程序 - 边界单元的争用

[英]Multithreaded Java Program for Conway's game of life - contention at the border cells

I am learning concurrent programming in java, and writing a simulation for Game of Life. 我正在学习java中的并发编程,并为Game of Life编写模拟。

Here is what I am thinking: 这就是我的想法:

  • Use int[][] to store the states of the cells 使用int [] []存储单元格的状态
  • partition the int[][] into t segments and use t worker threads 将int [] []分区为t段并使用t个工作线程
  • The t threads will read from their segment, calculate new values for all the cells in their segment and update the cells. t个线程将从其段读取,计算其段中所有单元的新值并更新单元。
  • Once they finished calculation they wait at a barrier for other workers to finish 一旦他们完成计算,他们就会等待其他工人的障碍完成
  • when the barrier is crossed the main thread will update the UI. 当屏障被越过时,主线程将更新UI。
  • the workers proceed to calculate the next state. 工人们继续计算下一个州。

Now there is going to be contention at the common borders of the segments. 现在,这些细分市场的共同边界将会发生争执。 If a thread overwrote the state of a border cell before its neighbor has read the previous value, the neighbor's calculation is going to be wrong. 如果一个线程在其邻居读取了之前的值之前覆盖了边界单元的状态,则该邻居的计算将是错误的。

What are my options? 我有什么选择?

  • Use callable instead of runnable and have the worker threads return the new value (instead of updating the segments themselves). 使用callable而不是runnable并让工作线程返回新值(而不是更新段本身)。 The main thread can update the matrix after the barrier is crossed. 穿过屏障后,主线程可以更新矩阵。 This option involves copying the results returned by worker threads into the matrix. 此选项涉及将工作线程返回的结果复制到矩阵中。
  • Use two barriers. 使用两个障碍。 The workers thread make a copy of the border cells from their neighbors' segments and wait at the first barrier. 工作者线程从邻居的段中复制边界单元格并在第一道屏障处等待。 Once this barrier is passed, they proceed to calculate the next states and update the segments in place. 一旦通过此障碍,他们就会继续计算下一个状态并更新到位。 Then they wait at the 2nd barrier. 然后他们在第二道屏障等候。 the main thread updates the UI. 主线程更新UI。

My question is, is there any other way to deal with the contention at the border cells that does not involve copying data or is more efficient that the above two options? 我的问题是,有没有其他方法可以处理边界单元中不涉及复制数据的争用, 还是更有效的上述两种选择? May be using ReaderWriterLock, volatile variable or other synchronizing mechanism? 可能是使用ReaderWriterLock,volatile变量还是其他同步机制?

UPDATE: So far the double buffering solution by Peter is the cleanest one. 更新:到目前为止,彼得双缓冲解决方案是最干净的解决方案 But I have a question. 但我有一个问题。 Since the two arrays are shared data and we are not using any synchronization (synchronized access or volatile variable), won't it create visibility problem ? 由于这两个数组是共享数据而我们没有使用任何同步(同步访问或volatile变量),它是否会产生可见性问题 Could multiple CPUs cache the array values and update only a part of the array with each iteration? 多个CPU可以缓存数组值并在每次迭代时只更新数组的一部分吗? Then the threads will get stale values for the border cells. 然后线程将获得边界单元格的陈旧值。 Is this possible? 这可能吗? If not, why. 如果没有,为什么。 If yes, how do I solve it? 如果是,我该如何解决? It seems declaring two arrays volatile will not make their individual elements volatile . 似乎声明两个数组volatile不会使它们各自的元素变得易变

I suggest having 2 int[][] arrays. 我建议有2个int [] []数组。 Let's call them A and B. A will hold the values of all odd numbered "ticks" and B will hold the even numbered ticks. 我们称它们为A和B. A将保留所有奇数编号“ticks”的值,B将保持偶数编号。

Initialize A to whatever your initial state looks like. 将A初始化为初始状态。 Then let your threads loose to calculate the next state of each cell, and place the results in the corresponding cell in B. Once all your threads are done, you have your new state in B. Now, use B to calculate the next state of each cell, and store the results in A. At any given time, one array will be read only, and the other write only, so there will never be any contention. 然后让你的线程松散以计算每个单元格的下一个状态,并将结果放在B中的相应单元格中。一旦完成所有线程,就可以在B中获得新状态。现在,使用B计算下一个状态每个单元格,并将结果存储在A中。在任何给定时间,一个数组将是只读的,另一个数组只写,因此永远不会有任何争用。

Advantages: 好处:

  • No copying of data compared to what you do now. 与您现在的操作相比,不会复制数据。 Only one write is happening per cell. 每个单元格只发生一次写入。
  • No having to worry about edge/corner cases, as the algorithm is simple. 不必担心边缘/角落情况,因为算法很简单。
  • No ongoing allocation of memory. 没有持续的内存分配。 Just allocate the two arrays when you start. 只需在启动时分配两个数组。

Disadvantages: 缺点:

  • You need to allocate twice as much memory. 你需要分配两倍的内存。

It doesn't answer your actual question, but my recommendation would be your first option... having the new values returned rather than having the worker threads update them. 它没有回答你的实际问题,但我的建议是你的第一个选择...返回新值而不是让工作线程更新它们。 I'd take it a step further and have the "aggregator" combine the results from the worker threads into a new board state array and then discard the first one. 我更进一步,让“聚合器”将工作线程的结果合并到一个新的板状态数组中,然后丢弃第一个。 My reasoning being that it will improve the overall readability of the logic, since there will be little to no "global interactions" you have to worry about. 我的理由是它会提高逻辑的整体可读性,因为你不得不担心“全球互动”几乎没有。

That being said, I'm a bit biased because I prefer to program in a functional manner except when there's a strong reason not to. 话虽如此,我有点偏颇,因为我更喜欢以功能性方式编程,除非有充分的理由不这样做。

I would try the following approach: 我会尝试以下方法:

  • Have the workers perform the calculations, but only write back the values to the inner cells. 让工作人员执行计算,但只将值写回内部单元格。
  • For border cells, store the results. 对于边框单元格,存储结果。
  • When the calculations are complete, wait at a barrier. 计算完成后,等待屏障。
  • When all workers are at the first barrier, release then and allow each to write the border cells. 当所有工作人员都处于第一道屏障时,然后释放并允许每个人写入边界单元格。
  • Wait at a second barrier while the UI updates 在UI更新时等待第二道屏障

Storage required for a nxm tile is 2 * (n + m - 1) , so generally larger tiles (8x8 or more) require proportionately less memory for the cached values. nxm贴所需的存储nxm2 * (n + m - 1) ,因此通常较大的nxm贴(8x8或更多)需要按比例减少缓存值的内存。

I just stumbled upon java.util.concurrent.Exchanger<V> . 我偶然发现了java.util.concurrent.Exchanger<V> It acts as a point of exchange. 它充当交换点。 I can probably use it to exchange lists of cell-states between adjacent threads. 我可以用它来交换相邻线程之间的单元状态列表。 That would be better than barrier because I need to synchronize only between adjacent workers. 这比屏障更好,因为我只需要在相邻工人之间进行同步。

To answer your update about the caching issue with double buffering, it is not a problem. 要回答有关双缓冲的缓存问题的更新,这不是问题。 CPUs have coherent cache, and they know when data has been changed in another cpu cache. CPU具有一致的缓存,并且知道何时在另一个cpu缓存中更改了数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM