La función IO suspendida nunca regresa.
Tengo dificultades para escribir un bucle de recepción de mensajes UDP para Android.
En el siguiente código, en receiveLoop
, la llamada a receiveMessages
nunca retorna y, por lo tanto, nunca ingreso al bucle de tratamiento de mensajes. Ten en cuenta que todavía puedo recibir paquetes, pero se detiene cuando el búfer del canal está lleno.
Esperaría que receiveMessages
devuelva inmediatamente, mientras que el bucle de bloqueo de E/S dentro de él seguiría funcionando para siempre.
class MySocketUDP(private val params: SocketParams) {
private val rcvSocket: DatagramSocket by lazy {
val sock = DatagramSocket(params.rcvPort)
sock.reuseAddress = true
sock.soTimeout = 1000
sock
}
suspend fun receiveMessages(channel: SendChannel<message>) {
withContext(Dispatchers.IO) {
val buf = ByteArray(MAX_MSG_SIZE)
while (true) {
val pkt = DatagramPacket(buf, buf.size)
try {
if (channel.isClosedForSend) {
break
}
rcvSocket.receive(pkt)
val msg = packetToMessage(buf, 0, pkt.length)
Log.d("SOCKET", "filling channel with $msg")
channel.send(msg)
} catch (ex: SocketTimeoutException) {
} catch (ex: CancellationException) {
break
}
}
}
}
}
class MyModel {
private suspend fun receiveLoop(socket: MySocketUDP) {
withContext(Dispatchers.Main) {
val channel = Channel<message>(16)
socket.receiveMessages(channel)
Log.d("MODEL", "Entering msg loop")
for (msg in channel) {
dispatchRcvMessage(msg)
}
}
}
}
- ¿Por qué
receiveMessages
nunca devuelve mientras se ejecuta en el despachadorIO
y es llamado desde el despachadorMain
? - ¿Necesito realmente iniciar un hilo para este tipo de trabajo de productor/consumidor?
- ¿Puedes mostrar cómo lograr un código de bloqueo largo de manera “amigable con las coroutines”?
Gracias
davy.ai
receiveMessages
nunca retorna porque ejecuta un bucle infinito con llamadas de bloqueo de E/S, y es llamado desde el despachadorMain
. Esto significa que el despachadorMain
está esperando a quereceiveMessages
se complete antes de continuar, peroreceiveMessages
nunca termina porque está bloqueado en la llamadarcvSocket.receive
. Esto resulta en un bloqueo.No, no es necesario iniciar un hilo separado. Puedes usar corutinas para manejar las llamadas de bloqueo de E/S sin bloquear el despachador
Main
.Aquí tienes un ejemplo de cómo puedes lograr esto de manera compatible con las corutinas:
En este ejemplo, utilizamos la función
withContext
para cambiar al despachadorIO
antes de realizar las llamadas de bloqueo de E/S. También capturamos otras excepciones que pueden ser lanzadas durante el bucle de recepción.En
MyModel
, creamos unCoroutineScope
con el despachadorMain
y unJob
. Lanzamos la funciónreceiveLoop
en una corutina y almacenamos elJob
resultante. Luego, podemos cancelar elJob
cuando necesitemos detener el bucle de recepción.Ten en cuenta que este ejemplo usa
launch
en lugar deasync
. No necesitamos retornar un resultado desdereceiveLoop
, por lo que no es necesario usarasync
. Si necesitas retornar un resultado, utilizaasync
en su lugar.