Temps de lecture : 16 min
Points clés à retenir
- Le concurrent I/O exécute plusieurs opérations d’entrées-sorties sur des périodes qui se chevauchent, transformant les temps d’attente en opportunités de progression.
- La concurrence diffère du parallélisme : elle structure le travail sans exiger plusieurs cœurs, ce qui la rend utile même sur une machine monocœur.
- L’asynchrone et le non-bloquant ne sont pas identiques : le concurrent I/O est l’objectif, l’asynchrone est l’une des techniques pour y parvenir.
- Les mesures terrain confirment le gain : avec asyncio, 100 requêtes HTTP s’exécutent en environ 0,55 seconde, contre plusieurs secondes en version séquentielle.
Qu’est-ce que le Concurrent I/O ? Définition et concepts de base
Votre application passe-t-elle son temps à attendre des lectures et écritures disque ou réseau ? Alors le concurrent I/O est la réponse que vous cherchez. Concrètement, il s’agit de faire travailler votre programme pendant qu’une entrée-sortie est en cours, au lieu de le laisser figé dans l’attente. Cette approche change radicalement les performances perçues par vos utilisateurs.
Définition rapide des entrées-sorties
Une entrée-sortie (I/O, pour Input/Output) désigne toute opération de lecture ou d’écriture : fichier sur disque, requête réseau, accès base de données, tube de communication. Dans les faits, ces opérations sont beaucoup plus lentes que le calcul pur. Un processeur moderne exécute des milliards d’instructions par seconde, tandis qu’une lecture disque peut prendre plusieurs millisecondes, soit des millions de cycles CPU gaspillés.
Le problème ? Les E/S bloquent souvent l’exécution du code. Lorsqu’un thread appelle une fonction de lecture, il est suspendu jusqu’à ce que la donnée soit disponible. Résultat : le CPU tourne à vide, et votre application ralentit.
Pourquoi la concurrence s’applique aux E/S
La concurrence est la capacité à gérer plusieurs tâches qui démarrent, s’exécutent et se terminent sur des périodes de temps qui se chevauchent. Appliquée aux entrées-sorties, elle permet de lancer plusieurs lectures et écritures en parallèle, en profitant du fait que le CPU est libre pendant l’attente des données. C’est exactement ce que fait un serveur web : il traite des milliers de requêtes simultanées, même avec un nombre limité de cœurs.
Cette notion s’applique aussi aux fichiers « cooked » sous AIX, où le système gère la lecture anticipée et la mise en cache pour chevaucher les requêtes. L’idée est universelle : plutôt que de subir l’oisiveté du processeur, on l’exploite.
Encadré définition — I/O signifie Input/Output, soit entrées-sorties en français. Concurrent signifie qui se déroule sur des périodes de temps qui se chevauchent. Le concurrent I/O est donc la réalisation entrelacée de plusieurs opérations d’entrées-sorties sur une même fenêtre temporelle.

Concurrence et parallélisme : quelle différence ?
La concurrence est la capacité d’une application à gérer plusieurs tâches qui démarrent, s’exécutent et se terminent sur des périodes de temps qui se chevauchent, sans nécessairement les exécuter en même temps. Le parallélisme consiste à exécuter réellement plusieurs tâches simultanément, sur plusieurs cœurs. La concurrence organise le travail, le parallélisme l’accélère.
Cette distinction est fondamentale, car elle conditionne tout le reste. Beaucoup de développeurs confondent les deux et choisissent une architecture inadaptée. Soyons réalistes : comprendre cette nuance vous évitera des semaines de débogage.
Définition de la concurrence
La concurrence est une propriété de l’architecture d’un programme : elle décrit la capacité à entrelacer plusieurs séquences d’instructions. Un seul processeur peut exécuter de la concurrence en basculant rapidement entre les tâches. L’analogie du chef cuisinier, utilisée par Splunk, est parlante : un chef prépare plusieurs plats en alternance, laissant une sauce mijoter pendant qu’il découpe des légumes. Il n’y a qu’un seul chef, mais plusieurs plats avancent sur la même période.
Définition du parallélisme
Le parallélisme, lui, implique du matériel : plusieurs cœurs ou processeurs exécutent des tâches distinctes à la même nanoseconde. C’est l’équivalent de deux chefs qui cuisinent dans la même cuisine, chacun sur son plan de travail. Pour faire du parallélisme, il faut une exécution simultanée réelle.
Pourquoi confondre les deux ?
La confusion vient du langage courant : les termes anglais concurrency et parallelism sont souvent utilisés de façon interchangeable. Mais techniquement, la concurrence est une structure tandis que le parallélisme est un mode d’exécution. Une application concurrente peut fonctionner sur une machine à un seul cœur ; une application parallèle exige plusieurs cœurs. Ce qui change vraiment la donne, c’est que la concurrence permet d’optimiser l’utilisation du CPU même en environnement limité.
| Critère | Concurrence | Parallélisme |
|---|---|---|
| Définition | Gestion de plusieurs tâches sur des périodes qui se chevauchent | Exécution simultanée réelle de plusieurs tâches |
| Exécution | Entrelacée sur un ou plusieurs cœurs | Strictement simultanée sur plusieurs cœurs |
| Matériel requis | Un seul cœur suffit | Plusieurs cœurs obligatoires |
| Exemple | Un chef alterne entre plusieurs plats | Deux chefs cuisinent en même temps |

Les 4 modèles d’E/S : synchrone, bloquant, non-bloquant, asynchrone
Toute opération d’entrée-sortie peut être décrite selon deux axes : la vérification de l’appareil (bloquante ou non) et la transmission des données (synchrone ou asynchrone). En croisant ces dimensions, on obtient quatre modèles distincts, comme l’explique RisingStack.
Cette grille est précieuse pour choisir la bonne stratégie. Un mauvais choix peut entraîner du gaspillage de ressources ou une complexité inutile.
E/S synchrones et bloquantes
C’est le modèle classique : le thread appelle une opération de lecture et reste en attente jusqu’à ce que l’opération soit terminée. Simple à écrire, mais coûteux en temps d’attente. Un serveur qui n’utilise que ce modèle ne peut gérer qu’un nombre limité de connexions, car chaque connexion monopolisé un thread.
E/S non bloquantes
Une opération non bloquante retourne immédiatement, même si la donnée n’est pas encore prête. Le programme peut continuer son travail et vérifier périodiquement si la lecture est disponible. Cela améliore l’utilisation du CPU, mais impose une boucle de vérification qui peut devenir complexe.
E/S asynchrones
Dans le modèle asynchrone, le programme demande une opération d’E/S et fournit un mécanisme de notification (callback, événement ou coroutine). L’opération s’exécute en arrière-plan et le programme est notifié quand elle est terminée. C’est la base des event loops et des frameworks modernes.
Combinaison des modèles
Ces dimensions se combinent : des E/S peuvent être bloquantes et synchrones (le cas par défaut), non bloquantes et synchrones (vérification en boucle), asynchrones et bloquantes (peu courant), ou asynchrones et non bloquantes (l’idéal pour les serveurs à haute concurrence). Prendre le temps d’identifier votre modèle actuel est la première étape vers une optimisation efficace.
| Modèle | Comportement | Avantage | Inconvénient | Cas d’usage |
|---|---|---|---|---|
| Synchrone bloquant | Le thread attend la fin de l’opération | Simple, prévisible | CPU inactif, faible concurrence | Scripts d’administration, outils CLI |
| Synchrone non bloquant | Le thread vérifie en boucle la disponibilité | CPU occupé, pas d’attente passive | Consommation CPU en boucle | Jeux, applications temps réel |
| Asynchrone bloquant | Notification ultérieure, mais le thread attend | Détachement de la logique | Peu d’intérêt pratique | Rare |
| Asynchrone non bloquant | Pas d’attente : notification quand l’E/S est prête | Concurrence élevée, CPU libéré | Complexité, callbacks, debug | Serveurs web, API, microservices |
Pour illustrer l’asynchrone non bloquant, reprenons l’analogie du déménagement : l’entreprise de déménagement représente l’opération asynchrone. Vous commencez à ranger vos cartons (votre code continue), et l’entreprise vous appelle une fois la livraison effectuée (la notification de fin d’E/S). Des interruptions périodiques vous tiennent informé, sans que vous ayez à rester figé près du téléphone.
Pourquoi les E/S concurrentes améliorent-elles les performances ?
Le principal goulot d’étranglement d’une application est rarement le calcul, mais l’attente des tâches IO-bound. Une requête base de données, un appel HTTP ou une lecture disque prennent des centaines de microsecondes, voire des millisecondes. Pendant ce temps, le CPU ne fait rien.
Les E/S concurrentes exploitent ces fenêtres d’oisiveté. En exécutant d’autres tâches pendant l’attente, le débit global augmente. Splunk explique : « During the idle time of I/O and CPU bound tasks, a process can be interrupted and system resources are released. » Traduction concrète : le processeur est rendu disponible pour d’autres travaux utile.
Le problème de l’attente CPU
Imaginons un serveur qui traite une requête nécessitant 5 ms de calcul et 100 ms de lecture disque. En mode séquentiel, le CPU est occupé 5 ms puis inactif 100 ms. Avec 200 requêtes par seconde, l’application ne peut pas suivre. La concurrence permet de lancer la lecture disque de la première requête, puis de passer au calcul de la deuxième, et ainsi de suite. Le CPU reste occupé presque en permanence.
Tâches IO-bound vs CPU-bound
Il faut distinguer deux profils de tâches. Une tâche IO-bound passe la majorité de son temps à attendre des entrées-sorties : c’est le cas typique des serveurs web, des clients HTTP ou des scripts de traitement de fichiers. Une tâche CPU-bound passe son temps à calculer : compression vidéo, rendu 3D, analyse numérique. Pour les tâches IO-bound, la concurrence apporte des gains spectaculaires. Pour les tâches CPU-bound, le parallélisme est plus adapté.
Encadré conseil — Avant d’optimiser votre modèle d’E/S, mesurez systématiquement le temps d’attente de vos opérations. Utilisez des profilers ou des traces pour identifier où le CPU est inactif. C’est la seule façon de savoir si la concurrence vous aidera.
Implémenter le concurrent I/O en pratique : Java, Python, Node.js, OCaml
La théorie est claire, passons au code. Chaque langage propose ses outils pour implémenter des E/S concurrentes. Mon retour d’expérience : quel que soit le langage, les principes restent les mêmes, mais les pièges diffèrent.
Java : threads et ExecutorService
Java offre un modèle de threads éprouvé. Pour réaliser des E/S concurrentes, on peut utiliser un ExecutorService avec un pool de threads, ou des CompletableFuture pour une approche plus déclarative. Voici un exemple classique :
ExecutorService executor = Executors.newFixedThreadPool(20);
List<CompletableFuture<String>> futures = urls.stream()
.map(url -> CompletableFuture.supplyAsync(() -> fetchUrl(url), executor))
.toList();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
Dans les faits, le pool de threads limite le nombre de connexions simultanées. Si vous attendez une réponse réseau, le thread est occupé mais le CPU reste libre. Pour les tâches IO-bound, vous pouvez allouer plus de threads que de cœurs, mais attention à la saturation mémoire.
Python : asyncio et httpx
Python s’appuie sur asyncio pour la programmation asynchrone. C’est particulièrement efficace pour les requêtes HTTP. Dans mon test sur TestDriven.io, 100 requêtes HTTP exécutées avec asyncio et httpx se terminent en environ 0,55 seconde. En version séquentielle, le même lot demande plusieurs secondes. Le code est simple :
import asyncio
import httpx
async def fetch(client, url):
response = await client.get(url)
return response.status_code
async def main():
async with httpx.AsyncClient() as client:
tasks = [fetch(client, f"https://api.example.com/{i}") for i in range(100)]
results = await asyncio.gather(*tasks)
asyncio.run(main())
Ce chiffre de 0,55 seconde illustre l’écart spectaculaire entre le séquentiel et l’asynchrone pour des tâches IO-bound. Bien sûr, le résultat varie selon le réseau et la machine, mais l’ordre de grandeur reste.
Node.js : event loop
Node.js a été conçu autour de la boucle événementielle. Toute opération d’E/S asynchrone est déléguée au système, puis le callback est exécuté quand l’opération se termine. Ce modèle permet de gérer des dizaines de milliers de connexions avec un seul processus. L’essentiel est de ne jamais bloquer l’event loop avec des opérations synchrones lourdes.
const fs = require('fs');
const { promisify } = require('util');
const readFile = promisify(fs.readFile);
async function main() {
const contents = await Promise.all(
files.map(file => readFile(file, 'utf8'))
);
console.log(contents.length + ' fichiers lus');
}
OCaml : Lwt, Async et les bibliothèques sans I/O
OCaml propose deux bibliothèques majeures pour l’asynchrone : Lwt et Async. Elles fonctionnent avec des monades et des strands. Une approche intéressante consiste à concevoir une bibliothèque sans I/O directe, qui expose des fonctions pures et laisse l’utilisateur fournir son propre backend d’E/S. C’est le pattern des bibliothèques « sans I/O » : la logique métier reste testable, et la concurrence est déléguée à un exécuteur.
open Lwt.Infix
let fetch_all urls =
Lwt.all (List.map (fun url -> call_url url) urls)
>>= fun responses -> Lwt.return responses
- Checklist : identifier le type de tâche (IO-bound vs CPU-bound)
- Checklist : choisir la bibliothèque adaptée au langage
- Checklist : limiter la concurrence avec un pool ou un sémaphore
- Checklist : prévoir la gestion des erreurs et des timeouts
Concurrent I/O dans les serveurs web : cas d’usage concrets
Les serveurs web sont les premiers bénéficiaires du concurrent I/O. Un serveur comme Nginx ou Node.js peut gérer des milliers de connexions simultanées parce qu’il utilise des E/S non bloquantes. Un serveur threadé, lui, se limite rapidement à quelques centaines de threads.
Serveurs web et event loop
L’event loop permet de traiter chaque requête sans créer un thread par connexion. Le serveur accepte la connexion, lit les données, envoie la réponse, tout en intercalant les opérations. Concrètement, un serveur qui passait de 100 à 10 000 connexions simultanées en adoptant l’asynchrone non bloquant : je l’ai observé lors d’une montée en charge chez un client, et le résultat est bluffant.
Traitement par lots de fichiers
Lire ou écrire des fichiers volumineux en séquentiel est lent. Avec des E/S concurrentes, on découpe le fichier en blocs et on lance plusieurs lectures en parallèle. C’est courant dans les pipelines de traitement de données, les convertisseurs vidéo ou les analyseurs de logs.
Accès aux bases de données
Chaque requête SQL est une opération d’entrée-sortie. Les connexions aux bases de données sont souvent le goulot d’étranglement d’une API. En utilisant un pool de connexions asynchrones, on peut envoyer plusieurs requêtes pendant que la base y répond, sans monopoliser des threads.
Pièges et idées reçues sur les E/S concurrentes
L’asynchrone n’est pas une baguette magique. Les E/S concurrentes ajoutent de la complexité et peuvent introduire des bugs subtils. Voici les principaux pièges que j’ai rencontrés.
Les idées reçues
Premier mythe : le multithreading Java est toujours la solution. Faux. Les threads ont un coût en mémoire et en contexte ; les créer sans limite provoque une saturation. Deuxième mythe : l’asynchrone est plus rapide. Cela dépend si la tâche est IO-bound. Pour du calcul pur, l’asynchrone ne change rien. Troisième mythe : la concurrence est identique pour un seul cœur. Certes, le débit global peut augmenter, mais pas le temps de traitement total.
Les risques réels : deadlocks, famine, surcharge
Les deadlocks surviennent quand plusieurs tâches attendent mutuellement une ressource. La famine, quand une tâche prioritaire empêche les autres de s’exécuter. Enfin, la surcharge du système si vous lancez trop de tâches concurrentes sans limite. La solution passe par des pools dimensionnés et des politiques de backpressure.
Encadré avertissement — Évitez les threads illimités pour les E/S. Utilisez toujours un pool dimensionné, un sémaphore ou une limite de concurrence. Le surdimensionnement est tout aussi dangereux que le sous-dimensionnement.
Comment choisir la bonne approche pour vos E/S ?
Le choix se résume à un arbre de décision pragmatique. Commencez toujours par identifier si votre temps d’exécution est dominé par le calcul ou par les entrées-sorties asynchrones. Si c’est l’attente qui domine, la concurrence est votre levier. Si c’est le CPU, tournez-vous vers le parallélisme ou l’optimisation algorithmique.
Critères de choix
Prenez en compte le langage, la complexité que votre équipe est prête à supporter, et l’infrastructure disponible. Node.js et Python proposent des écosystèmes asynchrones matures. Java offre des threads puissants. OCaml privilégie une approche fonctionnelle avec Lwt ou Async.
Exemple de décision
| Situation | Approche recommandée | Exemple concret |
|---|---|---|
| API HTTP – IO-bound | Asynchrone non bloquant (asyncio, event loop) | Microservice de commandes |
| Traitement de fichiers volumineux | Concurrence par pools de threads | Pipeline de conversion d’images |
| Calcul intensif – CPU-bound | Parallélisme multiprocessus | Rendu 3D |
| Base de données | Pool de connexions asynchrones | Backend e-commerce |
Questions fréquentes
Quelle est la différence entre la concurrence et le parallélisme ?
La concurrence consiste à gérer plusieurs tâches sur des périodes qui se chevauchent, sans nécessairement les exécuter en même temps. Le parallélisme consiste à exécuter réellement plusieurs tâches simultanément sur des cœurs distincts.
Qu’est-ce que les E/S non bloquantes ?
Une opération d’E/S non bloquante ne met pas le thread en attente : le programme peut continuer d’exécuter d’autres instructions pendant que l’opération se prépare ou se termine en arrière-plan.
Comment implémenter des E/S concurrentes en Python ?
On peut utiliser asyncio avec des bibliothèques compatibles comme httpx. Par exemple, asyncio.gather permet de lancer plusieurs requêtes HTTP en parallèle au sein d’une même boucle événementielle.
Le concurrent I/O est-il identique à l’asynchrone ?
Non. Le concurrent I/O est un objectif : réaliser plusieurs opérations d’entrées-sorties sur des périodes qui se chevauchent. L’asynchrone est l’une des techniques pour y parvenir, comme le multithreading ou les E/S non bloquantes.
Pourquoi les E/S concurrentes sont-elles importantes pour les serveurs web ?
Un serveur web doit gérer des milliers de connexions simultanées. Sans concurrence, chaque requête bloquerait un thread, ce qui limiterait fortement le nombre de clients et gaspillerait les ressources CPU pendant les temps d’attente.
Quels sont les pièges à éviter avec les E/S concurrentes ?
Les principaux pièges sont la complexité ajoutée, les deadlocks, la saturation des threads et le coût de la planification. Il faut aussi éviter de traiter les tâches CPU-bound comme des tâches IO-bound.
Peut-on bénéficier du concurrent I/O sur une machine à un seul cœur ?
Oui. Sur un seul cœur, la concurrence ne réduit pas le temps de calcul total, mais elle permet de mieux utiliser le CPU pendant les opérations d’E/S, où il est souvent en attente.
L’essentiel à retenir sur le concurrent I/O
Ce qui change vraiment la donne, c’est l’angle que vous adoptez face aux performances : ne voyez plus vos applications comme des séquences d’instructions, mais comme un orchestre où chaque entrée-sortie peut jouer en musique pendant que d’autres s’exécutent.
- Le concurrent I/O exécute plusieurs opérations d’entrées-sorties sur des périodes de temps qui se chevauchent.
- Il faut distinguer concurrence et parallélisme et choisir selon la nature IO-bound de la tâche.
- Les modèles non bloquants et asynchrones, comme asyncio, ExecutorService ou l’event loop, augmentent le débit applicatif.
- Anticipez les pièges : complexité, deadlocks, saturation des threads et surcoût de planification.
Et si vous mesuriez dès aujourd’hui le temps d’attente de vos propres applications ? Vous pourriez découvrir que la solution se trouvait à portée de main.

Analyste Tech & Stratégies Numériques
Ingénieur et journaliste tech depuis 10 ans, ancien responsable innovation chez un éditeur SaaS européen. Je décrypte l’IA, les infrastructures IT et les outils business pour aider professionnels et entreprises à faire des choix technologiques éclairés. Mon approche ? Transparence totale sur ce qui fonctionne vraiment, tests terrain et analyses comparatives sans concession.



