康威的生活游戏,每个细胞都是一个线索

时间:2017-12-01 01:06:37

标签: java multithreading java-threads conways-game-of-life

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

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

    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());
                }
            }
        }

还有一些其他的东西,这些都是为了简单起见,我不相信它们是必要的,而且Cell Rules类也是如此。单元格规则如下:

    /**
     * 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());               
                }
            }
        }

我不确定线程​​的实现在哪里,任何指导或解释都将不胜感激! 祝你有愉快的一天:)

1 个答案:

答案 0 :(得分:1)

首先,您的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设置为等于计算机中处理器核心的数量,或者您在测试中发现的任何值,以获得理想的性能。

您可以使用这些技术中的任何一种,或者使用不同的技术来分割线程(例如,可能是一种完美地分割工作负载的算法,而不是舍入到最接近的行数?)。重要的是简单地保持代码组织良好,以便于您的工作负载分割机制。

如果你只限于Java 7,所有编写的代码仍然可以使用,但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();
}