Multithreading Kotlin:使用非阻塞I/O阻塞协同路由
我正在尝试使用Kotlin协程来处理非阻塞I/O。场景如下:Multithreading Kotlin:使用非阻塞I/O阻塞协同路由,multithreading,kotlin,coroutine,Multithreading,Kotlin,Coroutine,我正在尝试使用Kotlin协程来处理非阻塞I/O。场景如下: 从线程1上运行的异步回调接收数据 在线程2中等待此数据,然后使用它 我当前的代码如下所示(为简洁起见简化): 据我所知,Kotlin协同程序应该能够帮助我摆脱倒计时锁。读完后,我能想到的是这样的东西: // Wait and consume data fun getData() = async(CommonPool) { latch.await() latch = CountDownLatch(1) curre
// Wait and consume data
fun getData() = async(CommonPool) {
latch.await()
latch = CountDownLatch(1)
currentData
}
fun processData() {
launch(CommonPool) {
while (true) {
runBlocking {
val data = getData().await()
// Consume data
}
}
}
}
private suspend fun yourSuspendMethod() {
var watcher: BroadcastReceiver? = null
try {
suspendCoroutineWithTimeout<Boolean>(TimeUnit.SECONDS.toMillis(5)) {
watcher = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if // your logic
it.resume(true)
}
}
context?.registerReceiver(watcher, IntentFilter(...))
//call a method that will trigger the broadcast receiver
}
} finally {
context?.unregisterReceiver(watcher)
}
}
我也试过了,结果也差不多。我显然不明白如何使用这些功能。您没有说在
onReceive()
中接收的数据是否可以并行处理。这是主要问题。如果是,您可以在onReceive()
中执行。如果不允许这样做,则允许对onReceive()
的每次调用在CommonPool
上启动一个任务,而无需任何协同路由。如果它们应该按顺序处理,那么最简单的方法是启动一个内部有循环的线程:
fun onReceive(data: Any) {
queue.put(data);
}
....
// loop in a thread
while(true) {
data = queue.take();
processData(data);
}
同样,不需要协同程序
通常,协程是一种语法糖,用来表示异步程序,就好像它是同步的一样。我不认为您的程序可以使用协同程序。在使用Android开发时,有一种非常常见的模式是使用
CountDownLatch
,有时您希望在处理BroadcastReceivers
时使异步实现同步,CountDownLatch
非常方便
private suspend fun yourSuspendMethod() {
val job = GlobalScope.async {
val latch = CountDownLatch(1)
val watcher = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if // your logic
latch.countDown()
}
}
try {
mContext?.registerReceiver(watcher, IntentFilter(...))
//call a method that will trigger the broadcast receiver
if (!latch.await(5, TimeUnit.SECONDS)) {
throw Exception("Failed .... on latch's timeout")
}
} finally {
mContext?.unregisterReceiver(watcher)
}
}
job.await()
}
这里有一件非常重要的事情,不要更改CoroutineScope的上下文,否则它们将在一个完全不同的地方运行,按照我上面提到的方式,它将成为scope/context的子对象
[编辑]
为了避免使用CountDownLatch
,我决定对这个问题多加思考。闩锁的问题是,当您调用latch.wait
时,它将停止当前线程,因此如果这是来自主线程的线程,则主线程将等待并超时,因为它没有给接收器一个被调用的机会。解决这个问题的一种方法是使用我上面使用的示例
在上面的示例中,我忘记了一件事:如果您想要单元测试并同步调用方的上下文,那么您需要注入上下文。如果您决定这样做,那么您的实现将变得更加复杂,并且您将不会使用协同程序的全部功能,因为您将创建额外的线程
因此,解决方案将结合使用和timeout
+SuspendCancelableCorroutine
,您可以使用以下扩展:
suspend inline fun <T> suspendCoroutineWithTimeout(
timeout: Long,
crossinline block: (Continuation<T>) -> Unit
) = withTimeout(timeout) {
suspendCancellableCoroutine(block = block)
}
suspend inline fun suspendCoroutineWithTimeout(
超时时间:长,
交叉内联块:(续)->单位
)=withTimeout(超时){
SuspendCancelableCoroutine(块=块)
}
您的方法如下所示:
// Wait and consume data
fun getData() = async(CommonPool) {
latch.await()
latch = CountDownLatch(1)
currentData
}
fun processData() {
launch(CommonPool) {
while (true) {
runBlocking {
val data = getData().await()
// Consume data
}
}
}
}
private suspend fun yourSuspendMethod() {
var watcher: BroadcastReceiver? = null
try {
suspendCoroutineWithTimeout<Boolean>(TimeUnit.SECONDS.toMillis(5)) {
watcher = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if // your logic
it.resume(true)
}
}
context?.registerReceiver(watcher, IntentFilter(...))
//call a method that will trigger the broadcast receiver
}
} finally {
context?.unregisterReceiver(watcher)
}
}
private suspend方法(){
var观察程序:广播接收器?=null
试一试{
suspendCoroutineWithTimeout(时间单位为秒,单位为5)){
watcher=object:BroadcastReceiver(){
覆盖接收(上下文:上下文?,意图:意图?){
如果//你的逻辑
it.resume(真)
}
}
上下文?.registerReceiver(观察者、意图过滤器(…)
//调用将触发广播接收器的方法
}
}最后{
上下文?.取消注册接收者(观察者)
}
}
就是这样。现在,协同程序可以在不停止调用线程的情况下发挥其神奇的作用,并且当作业被取消时,超时也将被取消。从问题中的代码中,很难理解您的目标是什么。请澄清什么样的外部函数返回什么。在这种情况下,我们需要知道什么API调用返回承诺以及返回承诺的类型。请将此信息添加到问题中,不要使用CountDownLatch。改用ArrayBlockingQueue。Let onReceive()调用队列。put();和processData()调用queue.take()而不是getData()。正确的答案取决于您如何启动异步操作,该操作稍后将调用您的
onReceive
回调,以及您是否希望回调仅被调用一次或多次。请澄清您的问题以获得正确的答案。因此,如果预计会多次调用该问题,那么使用CountDownLatch
的原始代码应该如何工作?例如,想想当第二次调用onReceive
时会发生什么,而getData
已经完成了第一行(使用wait
),但还没有执行第二行。我只是想弄清楚您想要实现什么,这在您的原始代码中是绝对不清楚的。