Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Kotlin:使用非阻塞I/O阻塞协同路由_Multithreading_Kotlin_Coroutine - Fatal编程技术网

Multithreading 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

我正在尝试使用Kotlin协程来处理非阻塞I/O。场景如下:

  • 从线程1上运行的异步回调接收数据
  • 在线程2中等待此数据,然后使用它
  • 我当前的代码如下所示(为简洁起见简化):

    据我所知,Kotlin协同程序应该能够帮助我摆脱倒计时锁。读完后,我能想到的是这样的东西:

    // 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
    ),但还没有执行第二行。我只是想弄清楚您想要实现什么,这在您的原始代码中是绝对不清楚的。