Introduction aux files d'attente de tâches en Kotlin
Dans le développement logiciel moderne, il est souvent nécessaire de traiter des tâches de manière asynchrone, notamment lors de l'utilisation de requêtes réseau, d'opérations d'entrée-sortie ou de calculs en arrière-plan. Les files d'attente de tâches (task queues) sont l'un des modèles clés qui permet d'organiser l'exécution ordonnée des tâches, en évitant le blocage du thread principal. En langage Kotlin, grâce à son intégration étroite avec la JVM et sa prise en charge native des coroutines, l'implémentation des files d'attente devient élégante et efficace.
Dans cet article, nous allons voir comment créer et gérer des files d'attente de tâches en Kotlin, en utilisant à la fois les approches classiques (basées sur les threads) et les fonctionnalités modernes des coroutines. Vous découvrirez différents types de files d'attente, leurs avantages et obtiendrez des exemples de code pratiques que vous pouvez immédiatement appliquer dans vos projets.
Approches principales pour implémenter des files d'attente de tâches
En Kotlin, il existe plusieurs façons d'implémenter des files d'attente de tâches. Le choix de l'approche dépend des exigences de performance, de complexité et de l'environnement utilisé.
1. Files d'attente basées sur les threads (BlockingQueue)
La méthode classique, utilisée en Java et entièrement compatible avec Kotlin. BlockingQueue du package java.util.concurrent fournit une file d'attente thread-safe avec des opérations bloquantes. Cette approche convient bien aux applications multithread où plusieurs workers traitent les tâches.
import java.util.concurrent.BlockingQueueimport java.util.concurrent.LinkedBlockingQueue
class ThreadBasedTaskQueue { private val queue: BlockingQueue<Runnable> = LinkedBlockingQueue() private val workers: List<Thread>
constructor(workerCount: Int) { workers = (1..workerCount).map { workerId -> Thread { while (true) { val task = queue.take() // se bloque jusqu'à ce qu'une tâche soit disponible try { task.run() } catch (e: Exception) { println("Worker $workerId error: ${e.message}") } } }.apply { start() } } }
fun submit(task: Runnable) { queue.put(task) }}
// Utilisationfun main() { val taskQueue = ThreadBasedTaskQueue(4) repeat(10) { index -> taskQueue.submit { println("Task $index executed by ${Thread.currentThread().name}") } } Thread.sleep(1000)}Avantages : simplicité, comportement prévisible, bonne isolation des tâches. Inconvénients : surcharge liée à la création de threads, nombre limité de threads dans le système.
2. Files d'attente basées sur les coroutines (Channel)
Kotlin Coroutines propose un mécanisme plus léger — Channel. Les canaux permettent de transférer des données entre les coroutines sans bloquer les threads. C'est une solution idéale pour les applications asynchrones.
import kotlinx.coroutines.*import kotlinx.coroutines.channels.Channel
class CoroutineTaskQueue(private val workerCount: Int = 4) { private val channel = Channel<suspend () -> Unit>(Channel.UNLIMITED)
fun start(scope: CoroutineScope) { repeat(workerCount) { workerId -> scope.launch { for (task in channel) { try { task() } catch (e: Exception) { println("Coroutine worker $workerId error: ${e.message}") } } } } }
suspend fun submit(task: suspend () -> Unit) { channel.send(task) }}
// Utilisationfun main() = runBlocking { val taskQueue = CoroutineTaskQueue(4) taskQueue.start(this)
repeat(10) { index -> taskQueue.submit { delay(100) pr