【问题标题】:Terribly slow synchronization同步很慢
【发布时间】:2016-05-26 20:12:48
【问题描述】:

我正在尝试在许多线程上编写生命游戏,1 个单元 = 1 个线程,它需要线程之间的同步,因此在其他线程未完成读取先前状态之前,没有线程会开始计算它的新状态。这是我的代码

        public class Cell extends Processor{
            private static int count = 0;
            private static Semaphore waitForAll = new Semaphore(0);
            private static Semaphore waiter = new Semaphore(0);
            private IntField isDead;
            public Cell(int n)
            {
                super(n);
                count ++;
            }
            public void initialize()
            {
                this.algorithmName = Cell.class.getSimpleName();
                isDead = new IntField(0);
                this.addField(isDead, "state");
            }
            public synchronized void step()
            {
                int size = neighbours.size();
                IntField[] states = new IntField[size];
                int readElementValue = 0;
                IntField readElement;
                sendAll(new IntField(isDead.getDist()));
                Cell.waitForAll.release();
    //here wait untill all other threads  finish reading
                    while (Cell.waitForAll.availablePermits() != Cell.count) {
                    }
    //here release semaphore neader lower 
                Cell.waiter.release();
                for (int i = 0; i < neighbours.size(); i++) {
                    readElement = (IntField) reciveMessage(neighbours.get(i));
                    states[i] = (IntField) reciveMessage(neighbours.get(i));
                }
                int alive = 0;
                int dead = 0;
                for(IntField ii: states)
                {
                    if(ii.getDist() == 1)
                        alive++;
                    else
                        dead++;
                }
                if(isDead.getDist() == 0)
                {
                    if(alive == 3)
                        isDead.setValue(1);
                    else
                        ;
                }
                else
                {
                    if(alive == 3 || alive == 2)
                        ;
                    else
                        isDead.setValue(0);
                }
                try {
                    while(Cell.waiter.availablePermits() != Cell.count)
                    {
                        ;
                      //if every thread finished reading we can acquire this semaphore
                    }
                    Cell.waitForAll.acquire();
                    while(Cell.waitForAll.availablePermits() != 0)
                        ;
//here we make sure every thread ends step in same moment
                    Cell.waiter.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

处理器 类扩展thread 并在运行方法中,如果我打开它调用step() 方法。好吧,它适用于少量单元格,但是当我运行大约 36 个单元格时,它开始变得非常慢,如何修复我的同步以使其更快?

【问题讨论】:

  • 请注意,除了个人实验之外,多线程并不是完成此类事情的好方法。正如您所发现的,由于线程开销,性能可能会非常糟糕,而单线程解决方案在整个板上从旧状态计算新状态可能会更好。
  • 是的,这是用于个人实验,但只有 36 个线程,而且它们进行的计算非常简单,这就是为什么我如此惊讶,也许有,肯定有更好的同步方法然后我有两个semaphores
  • 如果每个单元格有一个线程,那为什么实例方法Cell.step()需要同步呢?为什么多个线程需要在同一个Cell 上争夺对这个方法的访问权?
  • 我想通过sendall同步,所以每个单元格都有相同的数据
  • @whd,如果您对sendall 的评论是对我的回应,那么我认为您错过了重点。 Java 同步始终与特定对象的监视器相关。对于带有synchronized 关键字的实例方法,该对象就是调用该方法的对象。多个线程都在不同对象上调用相同的synchronized 方法,因此彼此根本不同步。

标签: java multithreading concurrency synchronization


【解决方案1】:

使用大量线程往往效率不高,但 36 并没有那么多,我认为它本身会产生一种差异,你会认为它“非常慢”。我认为这个问题更有可能是你的策略所固有的。特别是,我怀疑这种忙等待是有问题的:

Cell.waitForAll.release();
//here wait untill all other threads  finish reading
while (Cell.waitForAll.availablePermits() != Cell.count) {
}

忙等待总是是一个性能问题,因为您一遍又一遍地测试条件占用了 CPU。这种忙等待比大多数情况都糟糕,因为它涉及到同步对象的状态测试,这不仅有额外的开销,还会在线程之间引入额外的干扰。

您希望使用各种方法之一来使线程暂停执行,直到满足条件,而不是忙于等待。看起来您实际上所做的是创建了 CyclicBarrier 的穷人版本,因此您可以考虑使用 CyclicBarrier 本身。或者,由于这是一个学习练习,您可能会从学习如何使用 Object.wait()Object.notify()Object.notifyAll()(Java 的内置条件变量实现)中受益。

如果您坚持使用信号量,那么我认为您可以在没有忙等待的情况下做到这一点。使用信号量的关键是它能够(完全)获取表明线程可以继续进行的信号量,而不是可用许可的数量。如果您维护一个单独的变量来跟踪在给定点有多少线程在给定信号量上等待,那么到达该点的每个线程都可以确定是释放所有其他线程(并继续自己)还是通过尝试阻止获取信号量。

【讨论】:

  • 看来有很多东西要学,谢谢之前没听说过循环障碍!
猜你喜欢
  • 2012-08-31
  • 1970-01-01
  • 2020-06-19
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
相关资源
最近更新 更多