我有一个数据 struct ,它的成员不是线程安全的,调用者需要适当地锁定资源以进行读写.以下是一个最小的代码示例:

class ExampleResource : LockableProjectItem {
    override val readWriteLock: ReadWriteLock = ReentrantReadWriteLock()

    @RequiresReadLock
    val nonThreadSafeMember: String = ""
}

interface LockableProjectItem {
    val readWriteLock: ReadWriteLock
}

fun <T : LockableProjectItem, Out> T.readLock(block: T.() -> Out): Out {
    try {
        readWriteLock.readLock().lock()
        return block(this)
    } finally {
        readWriteLock.readLock().unlock()
    }
}

fun <T : LockableProjectItem, Out> T.writeLock(block: T.() -> Out): Out {
    try {
        readWriteLock.writeLock().lock()
        return block(this)
    } finally {
        readWriteLock.writeLock().unlock()
    }
}

annotation class RequiresReadLock

然后,呼叫ExampleResource.nonThreadSafeMember可能如下所示:

val resource = ExampleResource()
val readResult = resource.readLock { nonThreadSafeMember }

为了确保调用者知道需要锁定资源,我希望IDE对任何用@RequiresReadLock注释且没有用readLock块括起来的成员发出警告.有没有办法在IntelliJ中做到这一点,而不是为IDE编写一个定制插件?

推荐答案

我认为这是一种黑客行为,但使用context receivers可能会奏效.不过,我认为它们不是以这种方式使用的.

您可以声明一个伪object作为上下文接收方,并将其作为上下文接收方添加到属性中:

object ReadLock

class ExampleResource : LockableProjectItem {
    override val readWriteLock: ReadWriteLock = ReentrantReadWriteLock()

    // properties with context receivers cannot have a backing field, so we need to explicitly declare this
    private val nonThreadSafeMemberField: String = ""

    context(ReadLock)
    val nonThreadSafeMember: String
        get() = nonThreadSafeMemberField
}

然后在readLock,你通过了object:

fun <T : LockableProjectItem, Out> T.readLock(block: context(ReadLock) T.() -> Out): Out {
    try {
        readWriteLock.readLock().lock()
        return block(ReadLock, this)
    } finally {
        readWriteLock.readLock().unlock()
    }
}

备注:

  • 如果您try 在没有上下文接收方的情况下访问nonThreadSafeMember,则会出现错误:

    val resource = ExampleResource()
    val readResult = resource.nonThreadSafeMember //error
    
  • 您仍然可以通过执行以下操作来访问nonThreadSafeMember,而无需获取读锁定:

    with(ReadLock) { // with(ReadLock) doesn't acquire the lock, just gets the context receiver
        resource.nonThreadSafeMember // no error
    }
    

    但要accidentally%写这样的东西要难得多,我认为这就是你试图阻止的.

  • 如果您调用readLock内的另一个函数,并且想要访问该函数内的nonThreadSafeMember,则也应该将该函数标记为context(ReadLock).例如:

    fun main() {
        val resource = ExampleResource()
        val readResult = resource.readLock {
            foo(this)
        }
    }
    
    context(ReadLock)
    fun foo(x: ExampleResource) {
        x.nonThreadSafeMember
    }
    

    上下文接收器被传播通过.

Kotlin相关问答推荐

我可以检测一个函数是否在Kotlin中被递归调用(即,重入)吗?

Kotlin协程挂起继续线程

Kotlin -从列表中获取特定过滤器的唯一列表值

如何在Jetpack Compose中从领域查询中读取数据?

在Kotlin项目中使用Free Fair AspectJ插件(使用Gradle)

如何在 kotlin 中的数据类中为变量提供多种类型

为什么使用 return instance ?: synchronized(this) { instance ?: PreferenceParameterState(context) } 时无法获得单例?

Webpack 配置未应用于 kotlin 多平台react 项目

在协程上下文中重新抛出异常

Kotlin 接口类型参数

Kotlin 条件格式字符串

如何在 Android 的 Fragment 中使用 setUserVisibleHint

runBlocking 中的 deferred.await() 抛出的异常即使在被捕获后也被视为未处理

如何创建 Kotlin DSL - DSL 语法 Kotlin

Kotlin 静态函数:伴生对象,@JvmStatic @JvmField

无法解决:androidx.lifecycle:lifecycle-viewmodel-ktx:1.1.1

如何在Kotlin中创建无限长的序列

内联 onFocusChange kotlin

Kotlin:在何时使用枚举

函数引用和lambdas