使用RxJava的PublishSubject进行竞争条件

时间:2017-05-08 10:49:56

标签: rx-java2

当将PublishSubject与blockingGet()一起使用时,似乎存在订阅者未收到事件的竞争条件。 我在Kotlin中附加了一个基本的JUnit测试,它有两种方法。

rxTestBroken()使用PublishSubject显示损坏的行为。
rxTestOk()表明使用BehaviorSubject可以正常工作,因为后者会在订阅者没有及时订阅的情况下重放最后一个事件。

这种竞争条件来自哪里,并且正在使用BehaviorSubject正确修复?

import io.reactivex.Single
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.PublishSubject
import io.reactivex.subjects.Subject
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.util.concurrent.TimeUnit

class StateMachine(val stateSubject: Subject<Int>) {

  companion object {
    val STATE_IDLE = 1
    val STATE_READY = 2
  }

  val output = 10L
  var currentState = STATE_IDLE

  fun scheduleNextState(nextState: Int) {
    Thread(Runnable {
      currentState = nextState
      stateSubject.onNext(currentState)
    }).start()
  }

  fun start() = scheduleNextState(STATE_READY)
  fun stop() = scheduleNextState(STATE_IDLE)
}


class RxTest {

  fun stateOutput(stateSubject: Subject<Int>): Single<Int> {
    val stateMachine = StateMachine(stateSubject)

    val waitForIdle = stateSubject
        .startWith(stateMachine.currentState)
        .doOnNext {
          if (it != StateMachine.STATE_IDLE) { stateMachine.stop() }
        }
        .filter { it == StateMachine.STATE_IDLE }
        .firstOrError()

    val scanFile = stateSubject
        .doOnSubscribe { stateMachine.start() }
        .filter {
          when (it) {
            StateMachine.STATE_READY -> true
            StateMachine.STATE_IDLE -> false
            else -> throw RuntimeException("Wrong state $it")
          }
        }
        .firstOrError()
        .map { stateMachine.output.toInt() }
        .doFinally { stateMachine.stop() }

    return waitForIdle.flatMap { scanFile }.timeout(1, TimeUnit.SECONDS).onErrorReturnItem(-1)
  }

  @Test
  fun rxTestBroken() {
    for (i in 1..10000) {
      assertThat(stateOutput(PublishSubject.create<Int>()).blockingGet())
          .withFailMessage("worked $i times")
          .isEqualTo(10)
    }
  }

  @Test
  fun rxTestOk() {
    for (i in 1..10000) {
      assertThat(stateOutput(BehaviorSubject.createDefault(StateMachine.STATE_IDLE)).blockingGet())
          .withFailMessage("worked $i times")
          .isEqualTo(10)
    }
  }

}

0 个答案:

没有答案
相关问题