Как использовать сопрограмму Kotlin для вызова функции блокировки?

Я хотел бы использовать сопрограммы Kotlin для вызова функций блокировки.

Например, я хотел бы иметь функцию приостановки, которая блокировалась бы до тех пор, пока пользователь не ответит на модальное диалоговое окно или пока не вернется сетевой вызов, когда диалоговое окно и сетевые библиотеки не предоставляют suspend функции.

Я реализовал это с помощью Mutex.

Например, для сценария блокировки сетевого вызова я сделал что-то вроде этого:

class Connection {
    private val mutex = Mutex()
    
    public suspend fun receive(): ByteArray {
        mutex.lock()

        val buf = ByteArray(42)
        
        thread {
            sock.getInputStream().read(buf, 0, 42) // blocking
            mutex.unlock()
        }

        mutex.lock()

        return buf
    }
}

Оно работает.

Не обращайте внимания на проблемы, связанные с сетью, в приведенном выше коде (например, обработку ошибок, проверку количества фактически прочитанных байтов и т. Д.).

Также игнорируйте аспекты производительности при использовании потока (выделенный поток выше приведен только для примера).

С точки зрения использования Mutex для преобразования блокирующего потока в поток сопрограмм:

  1. Есть стандартный или лучший способ сделать это?
  2. Помимо сети или производительности - видите ли вы какие-либо недостатки в моем подходе?

person obe    schedule 11.10.2020    source источник
comment
Вы имеете в виду что-то вроде runBlocking, которое блокирует поток до завершения операции?   -  person Vitaliy-T    schedule 11.10.2020
comment
@ R'J похоже, что это может сработать :)   -  person obe    schedule 11.10.2020
comment
Тем не менее, блокировка потоков не рекомендуется для решения проблем с помощью Kotlin Coroutines. Они очень универсальны, и вы можете достичь результатов без блочных потоков, поэтому я предлагаю изучить то, что написали ребята ниже, в частности, используя диспетчер IO.   -  person Vitaliy-T    schedule 11.10.2020


Ответы (2)


Вы можете просто сделать:

suspend fun receive(): ByteArray {
    return withContext(Dispatchers.IO) {
        val buf = ByteArray(42)
        sock.getInputStream().read(buf, 0, 42) // blocking
        buf
    }
}

назовите его со своего Activity как:

lifecycleScope.launch{
    withContext(Dispatchers.Main) {
        //showLoading
        val result = receive()
        //hideloading
    }
}

функция приема будет запускаться в диспетчере ввода-вывода, который designed for offloading blocking IO tasks to a shared pool of threads см. подробнее о диспетчерах здесь.

person Nongthonbam Tonthoi    schedule 11.10.2020

Стандартный способ - использовать планировщик IO, который предназначен для большого пула потоков для обработки операций блокировки (как следует из названия, IO).

withContext(Dispatchers.IO) {
    sock.getInputStream().read(buf, 0, 42)
}

Это приостановит работу сопрограммы до завершения операции блокировки.

Обратной стороной вашего подхода является то, что порядок выполнения не совсем ясен и подвержен взаимоблокировкам. Например, если ваш блокирующий вызов вызывает исключение, мьютекс никогда не разблокируется, и сопрограмма застревает. Кроме того, он создает новые потоки для каждой блокирующей операции, а диспетчер ввода-вывода предназначен для повторного использования пула потоков, чтобы избежать этих затрат.

person Kiskae    schedule 11.10.2020