康威的生命游戏,每个细胞都是一根线

Conway's game of life with each cell being a thread

康威的生命游戏,每个细胞都是一个线程

大家好, 所以就像标题所说的那样,我必须制作一个程序来实现 Conway 的生命游戏中的线程,其中每个死细胞或活细胞都是一个线程。 我的第一个目标是简单地让游戏正常运行,我做到了(非常有趣的挑战) 所以我可以打印 20x20 网格,每个单元格都初始化为随机数 1 或 0,其中 1 是活的,0 是死的。 现在,我一直在观看关于如何使用线程的视频,但我仍然不确定我应该如何为每个单元实现它...... 我有 3 个 classes,Main、Cell 和 CellRules 我的 Cell class 看起来像:

    public class Cell implements Runnable 
    {
        public static int myCount = 0;
        private String name;
        private int neighbors;
        private int alive; // 0 is dead; 1 is alive.

        Random rand = new Random();


        public Cell (String nm)
        {
            name = nm;
            myCount = rand.nextInt(999);
            neighbors = 0;

            // 2 because it is exclusive
            this.setLife(rand.nextInt(2));
        }

            public void run()
        {
            while(Cell.myCount <= 10){
                try
                {
                    System.out.println("Expl Thread: " + (++Cell.myCount));
                    Thread.sleep(100);
                } catch (InterruptedException iex) 
                {
                    System.out.println("Exception in thread: 
                    "+iex.getMessage());
                }
            }
        }

里面还有一些其他的东西,真的是为了简单起见,我认为没有必要显示它们,单元格规则也是如此 class。单元格规则如下:

    /**
     * This function simply gets the number of neighbors for each cell, and saves the future generation
     * based on the number neighbors from arr to future array.
     * 
     * @param arr Arr that will be checked for neighbors
     * @param futureGen This array will keep track of the future generation
     * @param columns numbers of columns
     * @param rows numbers of rows
     */
        public void checkN(Cell [][] arr, Cell [][] futureGen, int columns, int rows)
        {
            for (int x = 0; x < rows; x++) 
            {
                for (int y = 0; y < columns; y++) 
                {               
                    arr[x][y].setNeighbors(0);

                    // Upper left corner
                    if (x == 0 && y == 0)
                        for (int i = 0; i <= 1; i++)
                            for (int j = 0; j <= 1; j++)
                                if (arr[x + i][y + j].getLife() == 1)
                                    arr[x][y].addNeighbor();

                    // Upper margin checks
                    if ((x == 0) && (y != 0 && y <= columns - 2))
                        for(int i = 0; i <= 1; i++)
                            for(int j = -1; j <= 1; j++)
                                if (arr[x + i][y + j].getLife() == 1)
                                    arr[x][y].addNeighbor();

                    // Upper right corner
                    if ((x == 0) && (y == columns - 1))
                        for(int i = 0; i <= 1; i++)
                            for(int j = -1; j <= 0; j++)
                                if (arr[x + i][y + j].getLife() == 1)
                                    arr[x][y].addNeighbor();

                    // Left margin checks
                    if ((y == 0) && (x != 0 && x <= rows - 2))
                        for (int i = -1; i <= 1; i++)
                            for (int j = 0; j <= 1; j++)
                                if (arr[x + i][y + j].getLife() == 1)
                                    arr[x][y].addNeighbor();


                    // Lower left corner
                    if ((x == rows - 1) && y == 0)
                        for (int i = -1; i <= 0; i++)
                            for (int j = 0; j <= 1; j++)
                                if (arr[x + i][y + j].getLife() == 1)
                                    arr[x][y].addNeighbor();

                    // Bottom margin checks
                    if ((x == rows - 1) && (y != 0 && y <= columns - 2 ))
                        for (int i = -1; i <= 0; i++)
                            for (int j = -1; j <= 1; j++)
                                if (arr[x + i][y + j].getLife() == 1)
                                    arr[x][y].addNeighbor();

                    // Lower right corner
                    if ((x == rows - 1) && (y == columns - 1))
                        for (int i = -1; i <= 0; i++)
                            for (int j = -1; j <= 0; j++)
                                if (arr[x + i][y + j].getLife() == 1)
                                    arr[x][y].addNeighbor();

                    // Right margin checks
                    if ((y == columns - 1) && (x != 0 && x <= rows - 2))
                        for (int i = -1; i <= 1; i++)
                            for (int j = -1; j <= 0; j++)
                                if (arr[x + i][y + j].getLife() == 1)
                                    arr[x][y].addNeighbor();

                    // Middle area checks ( can check all around now )!
                    if ((x > 0) && (x < rows - 1) && (y > 0) && (y < columns - 1) )
                        for (int i = -1; i <= 1; i++)
                            for (int j = -1; j <= 1; j++)
                                if (arr[x + i][y + j].getLife() == 1)
                                    arr[x][y].addNeighbor();

                    // Do not add yourself!
                    if (arr[x][y].getLife() == 1)
                        arr[x][y].subNeighbor();

                    // Get the new generation through the neighbors
                    if ((arr[x][y].getLife() == 1) && 
                        (arr[x][y].getNeighbors() < 2))
                        futureGen[x][y].setLife(0);
                    else if ((arr[x][y].getLife() == 1) && 
                             (arr[x][y].getNeighbors() > 3))
                        futureGen[x][y].setLife(0);
                    else if ((arr[x][y].getLife() == 0) && 
                             (arr[x][y].getNeighbors() == 3))
                        futureGen[x][y].setLife(1);
                    else
                        futureGen[x][y].setLife(arr[x][y].getLife());               
                }
            }
        }

我不确定线程​​的实现到底去了哪里,非常感谢任何指导或解释! 祝你有美好的一天:)

首先,您的 checkN 函数设计过度了。让我们稍微简化一下。

/**
 * This function simply gets the number of neighbors for each cell, and saves the future generation
 * based on the number neighbors from arr to future array.
 * 
 * @param arr Arr that will be checked for neighbors
 * @param futureGen This array will keep track of the future generation
 * @param columns numbers of columns
 * @param rows numbers of rows
 */
public void checkN(Cell [][] arr, Cell [][] futureGen, int columns, int rows) {
    for (int x = 0; x < rows; x++) {
        for (int y = 0; y < columns; y++) {               
            arr[x][y].setNeighbors(0);

            for(int i = -1; i <= 1; i++) {
                for(int j = -1; j <= 1; j++) {
                    if(i == 0 && j == 0) continue; //don't check self
                    if(x + i < 0 || x + i >= rows) continue; //bounds checking
                    if(y + j < 0 || y + j >= columns) continue; //bounds checking

                    if (arr[x + i][y + j].getLife() == 1)
                            arr[x][y].addNeighbor();
                }
            }

            // Get the new generation through the neighbors
            if(arr[x][y].getLife() == 1 &&
                (arr[x][y].getNeighbors() == 2 || arr[x][y].getNeighbors() == 3))
                futureGen[x][y].setLife(1);
            else if(arr[x][y].getLife() == 0 && arr[x][y].getNeighbors() == 3)
                futureGen[x][y].setLife(1);
            else
                futureGen[x][y].setLife(0);               
        }
    }
}

然后我们可以将其重构为一些额外的函数:

private void countNeighbors(Cell[][] arr, int row, int column, int rows, int columns) {
    Cell c = arr[row][column];
    c.setNeighbors(0);

    for(int i = -1; i <= 1; i++) {
        for(int j = -1; j <= 1; j++) {
            if(i == 0 && j == 0) continue; //don't check self
            if(row + i < 0 || row + i >= rows) continue; //bounds checking
            if(column + j < 0 || column + j >= columns) continue; //bounds checking

            if (arr[row + i][column + j].getLife() == 1)
                    c.addNeighbor();
        }
    }
}

private void evaluateNeighbors(Cell oldCell, Cell newCell) {
    if (oldCell.getLife() == 1 &&
        (oldCell.getNeighbors() == 2 || oldCell.getNeighbors() == 3))
        newCell.setLife(1);
    else if(oldCell.getLife() == 0 && oldCell.getNeighbors() == 3)
        newCell.setLife(1);
    else
        newCell.setLife(0);
}

public void checkN(Cell [][] arr, Cell [][] futureGen, int columns, int rows) {
    for (int row = 0; row < rows; row++) {
        for (int column = 0; column < columns; column++) {               
            countNeighbors(arr, row, column, rows, columns);

            evaluateNeighbors(arr[row][column], futureGen[row][column]);            
        }
    }
}

我们这样重构的原因马上就会明白。

那么回到最初的问题:我们在这个程序的什么地方插入线程?

您需要决定如何拆分线程。根据你的问题,听起来你想为每个被评估的单元格启动一个独立的线程。虽然理论上这种方法没有任何问题,但这并不能保证任何显着的加速,仅仅是因为在数千个单元格(即使是适度大小的网格,你也会很快命中)你将创建数千个线程,并且只有大型服务器才有足够的线程来利用它。

尽管如此,如果那是您想要做的,代码(Java 8 需要)如下所示:

public void checkN(Cell [][] arr, Cell [][] futureGen, int columns, int rows)
{
    ArrayList<Thread> threads = new ArrayList<>();
    for (int row = 0; row < rows; row++) {
        for (int column = 0; column < columns; column++) {
            Integer thread_local_row = row;
            Integer thread_local_column = column;
            Thread t = new Thread(() -> {
                countNeighbors(arr, thread_local_row, thread_local_column, rows, columns);
                evaluateNeighbors(arr[thread_local_row][thread_local_column], futureGen[thread_local_row][thread_local_column]);
            });
            t.start();
            threads.add(t);
        }
    }
    for(Thread t : threads)
        t.join();
}

最终结果是每个单元格都将接收到自己的专用线程。请注意,一旦代码被重构,我们几乎不需要更改。

但是,正如我提到的,就我们正在创建的线程数而言,这是过大的杀伤力。因此,另一种方法是为每一行创建一个新线程。

public void checkN(Cell [][] arr, Cell [][] futureGen, int columns, int rows)
{
    ArrayList<Thread> threads = new ArrayList<>();
    for (int row = 0; row < rows; row++) {
        Integer thread_local_row = row;
        Thread t = new Thread(() -> {
            for (int column = 0; column < columns; column++) {
                countNeighbors(arr, thread_local_row, column, rows, columns);
                evaluateNeighbors(arr[thread_local_row][column], futureGen[thread_local_row][column]);
            }
        });
        t.start();
        threads.add(t);
    }
    for(Thread t : threads)
        t.join();
}

这样更好,但当然,如果你有一个不平衡的网格,其中有很多行但很少有列,它会再次变得过大。

因此第三种选择是使线程数保持不变,并扩展每个线程的工作负载以适应线程数。

public void checkN(Cell [][] arr, Cell [][] futureGen, int columns, int rows)
{
    ArrayList<Thread> threads = new ArrayList<>();
    final int NUM_OF_THREADS = 8; //Or can be passed as an argument
    for(int tid = 0; tid < NUM_OF_THREADS; tid++) {
        Integer thread_local_row_start = tid * rows / NUM_OF_THREADS;
        Integer thread_local_row_end = (tid + 1) * rows / NUM_OF_THREADS;
        Thread t = new Thread(() -> {
            for (int row = thread_local_row_start; row < thread_local_row_end; row++) {
                for (int column = 0; column < columns; column++) {
                    countNeighbors(arr, row, column, rows, columns);
                    evaluateNeighbors(arr[row][column], futureGen[row][column]);
                }
            }
        });
        t.start();
        threads.add(t);
    }
    for(Thread t : threads)
        t.join();
}

此选项往往是最高性能的,因为您可以将 NUM_OF_THREADS 设置为等于机器中处理器内核的数量,或者您在测试中找到的任何值以产生理想性能。

您可以使用这些技术中的任何一种,或者想出一种不同的技术来拆分线程(例如,也许一种算法可以完美地拆分工作负载,而不是四舍五入到最近的行数?) .重要的部分只是让您的代码组织得足够好,以促进您的工作负载拆分机制。

如果你限于Java7,所有写的代码仍然可以使用,但是lambda结构需要换成Anonymous Inner Class,任何变量需要在线程体中使用需要制作final:

public void checkN(final Cell [][] arr, final Cell [][] futureGen, final int columns, final int rows)
{
    ArrayList<Thread> threads = new ArrayList<>();
    for (int row = 0; row < rows; row++) {
        for (int column = 0; column < columns; column++) {
            final Integer thread_local_row = row;
            final Integer thread_local_column = column;
            Thread t = new Thread(new Runnable() {
                public void run() {
                    countNeighbors(arr, thread_local_row, thread_local_column, rows, columns);
                    evaluateNeighbors(arr[thread_local_row][thread_local_column], futureGen[thread_local_row][thread_local_column]);
                }
            });
            t.start();
            threads.add(t);
        }
    }
    for(Thread t : threads)
        t.join();
}