首页 文章

如何使用暂停然后恢复或替代方法在线程之间同步共享数据

提问于
浏览
2

我正在编写一个游戏,其中一个线程 - GameThread - 永远循环,更新我所有的精灵,渲染它们,然后再睡一段时间再做一遍 . 我还有一个定制的事件处理程序,处理按键等 .

在大多数情况下,这一切都很好 . 但是,如果在GameThread渲染时抛出一个事件,我会遇到问题 . 在极少数情况下,处理事件的处理程序可能会同时更改需要渲染的内容,从而影响GameThread渲染的结果 .

为了避免这种情况,我希望事件处理程序立即暂停GameThread,处理事件,然后恢复GameThread .

  • suspend() / resume() 方法符合我的需求,但已被弃用 . 然而,就我而言,由于死锁的可能性很小,无论如何使用它们都是安全的吗?

  • 如果不是,我还有哪些其他替代品没有大量的开销?

我已经看到一个建议,即通过在要暂停的线程中设置一个标志来请求线程暂停 . 然而,在我的情况下,我不认为这是一个可行的选项,因为GameThread循环在迭代循环期间可能需要一段时间 . 在我完成循环之前,我将无法检查标志,到那时为时已晚 .

我需要立即暂停,否则用户会注意到事件处理的延迟 .

3 回答

  • 1

    suspend()/ resume()方法适合我的需要,但它们已被弃用 . 然而,就我而言,由于死锁的可能性很小,无论如何使用它们都是安全的吗?

    显然,如果存在零死机会,那么它是安全的 . 但是有各种意想不到的方法可以解决僵局 . 例如,你可能碰巧在初始化一个类时暂停一个线程......这会使任何其他尝试引用该类的静态字段的线程死锁 . (这是JVM指定行为的结果 . 还有其他地方没有指定引擎盖下的锁定/同步 . 足够公平 . 它不需要......除非你正在考虑使用这些弃用的方法 . )

    因此,现实情况是,确定(证明)它是否真的安全是非常困难的 . 如果你无法确定这一点,那么这是一件有潜在风险的事情 . 这就是为什么这些方法已被弃用 .

    (严格来说,这不是死锁 . 死锁是线程永远无法继续的情况 . 在这种情况下,如果你可以恢复暂停的线程,其他线程就可以继续 . )

  • 0

    如果要同步对资源的访问,请使用ReentrantLock:

    ReentrantLock sync = new ReentrantLock();

    您必须将该锁定传递给您要访问共享数据的每个可运行的 .

    然后,在您访问相关资源的每个位置,您将使用该共享锁对象,并获取并释放锁(即,您的关键部分):

    sync.lock();
    
    try {
      // critical section code here
    }
    finally {
      sync.unlock();
    }
    

    这是java中非常标准的并发编程 . 请记住,“lock”是一种阻塞方法,因此您可能希望使用“tryLock”,它允许您尝试获取锁定,但返回一个关于您是否实际获得锁定的布尔值:

    if (sync.tryLock()) {
       try {
         //critical section
       }
       finally {
         sync.unlock();
       }
     }
    

    有一个版本的“tryLock”会等待一段时间,然后才会放弃尝试获取锁并返回false值 .

  • 1

    通常,你会做一些线程同步:

    这将允许您执行您正在执行的两项操作之一:在游戏渲染线程中渲染或根据您的事件执行更改 .

    您面临的问题似乎是您的渲染代码花费的时间太长,以至于您实际上无法获得平滑的体验(即,在渲染某些内容时,很多事件可能会堆积起来进行处理) . 在这种情况下,您应该使用可以快速完成并同步它们的独立部分进行渲染 .

    没有任何代码我不能给你一个具体的建议,但一般来说它看起来像这样:

    List<Shape> shapesToRender;
    Object lockObject = new Object(); // Note this must be somehow shared between two threads
    
    // Your rendering thread method
    public void renderForever() {
      while(true) {
        for(Shape shape: shapesToRender) {
          synchronized(lockObject) {
            render(shape);
          }
        }
      }
    }
    
    // One of your event handlers
    public void handleEvent(Event event) {
      synchronized(lockObject) {
        // Process event somehow, e.g. change the color of some of the shapes
        event.getShape().setColor(Color.RED);
      }
    }
    

    有了上述,要么:

    • 您将呈现一个形状(并且所有事件处理程序将等待完成),或者

    • 你的一些事件处理程序将会做某事(你的渲染线程将等待它完成)

    您应该更深入地查看此Java跟踪:

    因为还有其他解决方案,例如使用锁定对象:

    或并发集合:

    根据您的问题,可能更容易,最重要的是,经过严格测试的解决方案可以让您以标准方式执行某些操作,从而避免在推出自定义线程代码时可能遇到的所有陷阱 .

    希望这可以帮助 .

相关问题