Coroutines Android 4.1:阻塞队列后后续启动不工作
Coroutines Android 4.1: subsequent launches not working after blocking queue
如果我 运行 一个 launch
里面有一个阻塞队列阻塞,那么在那之后没有其他 launch
会 运行。这只发生在 Android 4.1 上,我用 Android 6.0.1 和 7.0 测试的其他设备工作正常。这是一个例子:
class MainActivity : AppCompatActivity() {
private val blockingQueue = ArrayBlockingQueue<String>(10)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launch {
Log.d(javaClass.simpleName, "TEST 1")
}
launch {
blockingQueue.take().run {
Log.d(javaClass.simpleName, "TEST 2")
}
}
launch {
Log.d(javaClass.simpleName, "TEST 3")
}
}
}
输出:
05-15 12:09:39.707 4337-4361/org.testcoroutines D/StandaloneCoroutine: TEST 1
TEST 3
从未被记录。但是,如果我将 "blocking" launch
替换为 thread
,它会起作用。
如果我通过在队列上放置一个元素来解除对队列的阻塞,那么其余的启动现在会执行 运行。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launch {
Log.d(javaClass.simpleName, "TEST 1")
}
launch {
blockingQueue.take().run {
Log.d(javaClass.simpleName, "TEST 2 $this")
}
}
launch {
Log.d(javaClass.simpleName, "TEST 3")
}
thread {
Thread.sleep(2000)
Log.d(javaClass.simpleName, "TEST WAKE UP")
blockingQueue.put("WAKE UP!")
}
}
输出:
05-15 12:10:33.367 4471-4492/org.testcoroutines D/StandaloneCoroutine: TEST 1
05-15 12:10:35.387 4471-4493/org.testcoroutines D/MainActivity: TEST WAKE UP
05-15 12:10:35.387 4471-4492/org.testcoroutines D/String: TEST 2 WAKE UP!
05-15 12:10:35.387 4471-4492/org.testcoroutines D/StandaloneCoroutine: TEST 3
知道为什么会发生这种情况以及如何解决它吗?
ArrayBlockingQueue.take
操作阻塞。它阻塞调用者线程并且不允许它用于任何其他事情。在 "small" 设备上,默认线程池中可能只有一个后台线程,当您阻止该线程时,不会发生任何其他有用的事情。协程旨在与不阻塞线程的非阻塞(异步)API 一起工作。
对于协同程序,您应该使用 channels,它的作用与阻塞队列对线程的作用大致相同。通道不会阻塞调用者线程,但 suspend 调用协程,因此允许对多个 运行 协程使用相同的线程。
您可以在 Guide to kotlinx.coroutines by example 中找到有关频道的更多信息。
如果我 运行 一个 launch
里面有一个阻塞队列阻塞,那么在那之后没有其他 launch
会 运行。这只发生在 Android 4.1 上,我用 Android 6.0.1 和 7.0 测试的其他设备工作正常。这是一个例子:
class MainActivity : AppCompatActivity() {
private val blockingQueue = ArrayBlockingQueue<String>(10)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launch {
Log.d(javaClass.simpleName, "TEST 1")
}
launch {
blockingQueue.take().run {
Log.d(javaClass.simpleName, "TEST 2")
}
}
launch {
Log.d(javaClass.simpleName, "TEST 3")
}
}
}
输出:
05-15 12:09:39.707 4337-4361/org.testcoroutines D/StandaloneCoroutine: TEST 1
TEST 3
从未被记录。但是,如果我将 "blocking" launch
替换为 thread
,它会起作用。
如果我通过在队列上放置一个元素来解除对队列的阻塞,那么其余的启动现在会执行 运行。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launch {
Log.d(javaClass.simpleName, "TEST 1")
}
launch {
blockingQueue.take().run {
Log.d(javaClass.simpleName, "TEST 2 $this")
}
}
launch {
Log.d(javaClass.simpleName, "TEST 3")
}
thread {
Thread.sleep(2000)
Log.d(javaClass.simpleName, "TEST WAKE UP")
blockingQueue.put("WAKE UP!")
}
}
输出:
05-15 12:10:33.367 4471-4492/org.testcoroutines D/StandaloneCoroutine: TEST 1
05-15 12:10:35.387 4471-4493/org.testcoroutines D/MainActivity: TEST WAKE UP
05-15 12:10:35.387 4471-4492/org.testcoroutines D/String: TEST 2 WAKE UP!
05-15 12:10:35.387 4471-4492/org.testcoroutines D/StandaloneCoroutine: TEST 3
知道为什么会发生这种情况以及如何解决它吗?
ArrayBlockingQueue.take
操作阻塞。它阻塞调用者线程并且不允许它用于任何其他事情。在 "small" 设备上,默认线程池中可能只有一个后台线程,当您阻止该线程时,不会发生任何其他有用的事情。协程旨在与不阻塞线程的非阻塞(异步)API 一起工作。
对于协同程序,您应该使用 channels,它的作用与阻塞队列对线程的作用大致相同。通道不会阻塞调用者线程,但 suspend 调用协程,因此允许对多个 运行 协程使用相同的线程。
您可以在 Guide to kotlinx.coroutines by example 中找到有关频道的更多信息。