【问题标题】:Assertions or Annotations for thread correctness线程正确性的断言或注释
【发布时间】:2016-08-21 13:30:50
【问题描述】:

我正在作为一个使用多线程的项目的一部分进行编码,并且我正在尝试寻找方法来检测我的代码中的线程错误。 是否有一些现有的工具可以帮助我做到这一点?

例如- 断言我的方法正在被正确的线程调用 或者 某种带有注释的静态检查,类似于@Nullable 和@NotNull,以检测我的代码何时从错误的线程调用方法。

虽然项目是多线程的,但几乎不需要同步,因为不同的线程不会访问相同的对象,它们有自己的实例。 广义上讲,有四个线程同时运行

  • 服务器线程 = 为一个或多个维护游戏状态 客户
  • 客户端线程 = 处理用户输入,维护本地 复制/缓存服务器数据以进行渲染
  • NetworkMessage 线程 = 处理传入/传出消息 服务端和客户端之间
  • 渲染线程 = 将本地数据处理为渲染信息 显卡

这些类有时仅用于其中一个线程(例如,用户输入轮询仅限客户端),有时它们用于多个线程(例如,射弹的计算移动同时在客户端和服务器上使用相同的代码以减少感知滞后)。好几次我从错误的线程中调用了一个方法,导致了微妙且不可重复的错误和非常严重的显示器屏幕损坏(来自我的拳头)

到目前为止我想到的是这样的:

public void myMethodThatAssumesClientThreadOnly() {
  assert checkThread(CLIENT);
  // can now happily call other client-thread code without fear 
}

但我更喜欢类似于@Nullable 的静态检查

例如

@Thread(CLIENT)
void myClientMethod() {
  //client-only stuff here
}

@Thread(SERVER)
void myServerMethod() {
  //server-only stuff here
}

@Thread(CLIENT + SERVER)
void myClientAndMethod() {
  myClientMethod();  // error- server thread might call client method 
}

不幸的是,作为一个注释新手,我不知道这是容易还是真的很难。

有什么建议吗?我无法想象我是第一个寻找这种东西的人。

TGG

【问题讨论】:

  • android 注释项目对此有一些支持——您可以将函数标记为会议是否在 ui 线程上。我不是大多数图书馆的粉丝,但你应该能够看看他们是如何做到的,并可能对其进行扩展

标签: java multithreading annotations assertions


【解决方案1】:

Checker Framework 支持创建用于验证程序正确性的编译时静态检查器。它的GUI Effect Checker 与您想要的相似。这是its manual的节选:

最常见的与 GUI 相关的错误之一是无效的 UI 更新或无效的线程访问:直接从后台线程访问 UI。

如果后台线程访问诸如 JPanel 之类的 UI 元素(通过调用 JPanel 方法或读取/写入 JPanel 的字段),则 GUI 框架会引发异常以终止程序。

程序员很难记住哪些方法可以在哪些线程上调用。 GUI 效果检查器解决了这个问题。程序员注释每个方法以表明是否:

  • 它不访问任何 UI 元素(并且可以在任何线程上运行)。
  • 它可以访问 UI 元素(并且必须在 UI 线程上运行)。

GUI 效果检查器静态强制 UI 方法仅从正确的线程调用。

GUI 效果检查器经过调整以检测和防止 GUI 线程错误,而您则担心客户端-服务器线程错误。但是,原理是相同的,您应该能够根据您的需要调整 GUI 效果检查器,只需进行相对较少的更改。

有一个paper 讨论使用 GUI 效果检查器的案例研究。

另一种方法是采用bug finder 来查找多线程应用程序中的错误。与 GUI 效果检查器不同,它没有保证没有线程错误。不过在实践中是有效的,而且不需要你在程序中写任何注解。

最后,Checker Framework 还包含一个Lock Checker,以确保正确同步。这有助于防止并发错误,尽管它与您对线程安全的主要担忧是正交的。

【讨论】:

    【解决方案2】:

    这将断言方法 foobar() 被正确的线程调用...

    SomeType foobar(...) {
        assert(Thread.currentThread() == theCorrectThread);
        ...
    }
    

    ...如果在您设置的第一个 foobar() 调用之前的代码中某处

    Thread theCorrectThread = new Thread(...);
    

    但我更喜欢类似于@Nullable 的静态检查

    我自己对注释知之甚少。我知道它们可用于将元信息附加到已编译的类,并且我知道程序可以通过调用 Class 对象的方法在运行时获取该信息,但如果有任何方式注释可以定义 compile-time 行为,这超出了我的理解范围。

    无论如何,这可能是一个有争议的问题。当编译器正在处理一个 .java 文件时,它无法判断哪些线程可能执行它包含的代码。

    【讨论】:

    • 注解可以定义编译时的行为——你可以在编译时通过反射读取注解。
    • @GabeSechan,这很有趣,但是如何编写在编译时执行的 Java 代码呢?那会很有趣,但我从来没有看到任何暗示可以做到这一点。
    • 我打错了,我的意思是运行时。但是您也可以在编译时使用注释,这是一个预编译步骤,生成然后编译的代码。这是一种相当常见的技术。
    猜你喜欢
    • 2018-07-18
    • 1970-01-01
    • 2018-02-04
    • 2011-03-14
    • 2012-09-12
    • 2020-11-29
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    相关资源
    最近更新 更多