Comment Lire un Code QR en Mode Async et Multithreading en C
Le scan QR à un seul thread bloque le thread appelant pendant la durée de chaque décodage d'image.
Dans un gestionnaire de bouton WPF, cela fige l'interface utilisateur jusqu'à la fin du décodage. Lors du traitement par lots de centaines d'images, les cœurs du processeur restent inactifs alors qu'ils pourraient fonctionner en parallèle. La méthode ReadAsync d'IronQR décharge les lectures individuelles vers une tâche en attente, et la méthode standard Read fonctionne avec Parallel.ForEach et Task.WhenAll pour le débit par lots.
Ce guide explique comment traiter les codes QR de manière asynchrone, répartir les lectures par lots sur les cœurs du processeur et combiner les deux modèles pour les pipelines à haut volume.
Démarrage rapide : Traiter des QR Codes de manière asynchrone
Charger une image et attendre les résultats décodés sans bloquer le thread appelant.
-
Installez IronQR avec le Gestionnaire de Packages NuGet
PM > Install-Package IronQR -
Copiez et exécutez cet extrait de code.
using IronQr; using IronSoftware.Drawing; var input = new QrImageInput(AnyBitmap.FromFile("ticket.png")); IEnumerable<QrResult> results = await new QrReader().ReadAsync(input); Console.WriteLine(results.First().Value); -
Déployez pour tester sur votre environnement de production.
Commencez à utiliser IronQR dans votre projet dès aujourd'hui avec un essai gratuit
Flux de travail minimal (5 étapes)
- Téléchargez la bibliothèque IronQR C# pour le traitement QR asynchrone
- Utilisez
ReadAsyncpour les lectures uniques non bloquantes. - Utilisez
Parallel.ForEachpour le traitement par lot lié au CPU - Combinez avec
SemaphoreSlimpour des pipelines de concurrence limitée - Collectez les résultats de
IEnumerableet affichez les valeurs décodées
Lecture asynchrone des codes QR
ReadAsync renvoie une tâche pouvant être attendue, ce qui la rend compatible avec les gestionnaires d'événements WPF/MAUI, les actions de contrôleur ASP.NET ou toute méthode asynchrone. L'entrée doit être construite à partir d'une image bitmap ; il n'y a pas de surcharge de chemin de fichier.
L'écriture est synchrone et ne comporte aucune variante asynchrone. Pour éviter de bloquer le thread pendant les E/S de fichiers, enveloppez l'étape d'enregistrement dans File.WriteAllBytesAsync() en utilisant les octets bruts exportés de la bitmap.
Entrée
Un badge d'événement à code QR scanné et régénéré pour illustrer le modèle de lecture et d'écriture asynchrone.
:path=/static-assets/qr/content-code-examples/how-to/async-and-multithreading/async-read-write.cs
using IronQr;
using IronQr.Enum;
using IronSoftware.Drawing;
// --- Async read: non-blocking QR decode ---
var inputBmp = AnyBitmap.FromFile("event-badge.png");
var imageInput = new QrImageInput(inputBmp, QrScanMode.OnlyDetectionModel);
var reader = new QrReader();
IEnumerable<QrResult> results = await reader.ReadAsync(imageInput);
foreach (QrResult result in results)
{
Console.WriteLine($"[{result.QrType}] {result.Value}");
}
// --- Async-wrapped save: QrWriter.Write() and QrCode.Save() are synchronous ---
QrCode qrCode = QrWriter.Write("https://ironsoftware.com");
AnyBitmap qrImage = qrCode.Save();
// Save the bitmap bytes asynchronously (not an IronQR API — standard .NET async I/O)
byte[] pngBytes = qrImage.ExportBytes();
await File.WriteAllBytesAsync("output-qr.png", pngBytes);
Imports IronQr
Imports IronQr.Enum
Imports IronSoftware.Drawing
Imports System.IO
' --- Async read: non-blocking QR decode ---
Dim inputBmp = AnyBitmap.FromFile("event-badge.png")
Dim imageInput = New QrImageInput(inputBmp, QrScanMode.OnlyDetectionModel)
Dim reader = New QrReader()
Dim results As IEnumerable(Of QrResult) = Await reader.ReadAsync(imageInput)
For Each result As QrResult In results
Console.WriteLine($"[{result.QrType}] {result.Value}")
Next
' --- Async-wrapped save: QrWriter.Write() and QrCode.Save() are synchronous ---
Dim qrCode As QrCode = QrWriter.Write("https://ironsoftware.com")
Dim qrImage As AnyBitmap = qrCode.Save()
' Save the bitmap bytes asynchronously (not an IronQR API — standard .NET async I/O)
Dim pngBytes As Byte() = qrImage.ExportBytes()
Await File.WriteAllBytesAsync("output-qr.png", pngBytes)
Sortie
Le terminal affiche le type et la valeur du QR décodé au format [QrType] Value, puis confirme que output-qr.png a été enregistré.
QrScanMode.Auto exécute à la fois la détection ML et une passe de numérisation de base, en remplissant la valeur décodée et le type QR dans chaque résultat. OnlyDetectionModel est plus rapide mais ne renvoie que les coordonnées de la boîte englobante, laissant le champ de valeur vide. Utilisez Auto chaque fois que le contenu encodé est nécessaire.
Traitement des codes QR avec multithreading
Pour les images qui peuvent être décodées indépendamment, Parallel.ForEach répartit le travail sur les cœurs de processeur disponibles. Une instance QrReader séparée par itération est la valeur par défaut sûre, car IronQR ne fournit aucune garantie explicite de sécurité des threads pour les instances de lecteur partagées.
Entrée
Quatre des dix images de test de code QR utilisées dans le scan par lots parallèle. Chaque image encode une URL et est lue à partir du dossier qr-images/ au moment de l'exécution.
Image 1 (Lot 1 sur 10)
Image 2 (Lot 2 sur 10)
Image 3 (Lot 3 sur 10)
Image 4 (Lot 4 sur 10)
:path=/static-assets/qr/content-code-examples/how-to/async-and-multithreading/parallel-batch.cs
using IronQr;
using IronQr.Enum;
using IronSoftware.Drawing;
using System.Collections.Concurrent;
using System.Diagnostics;
string[] files = Directory.GetFiles("qr-images/", "*.png");
var allResults = new ConcurrentBag<(string File, string Value)>();
int failCount = 0;
var sw = Stopwatch.StartNew();
Parallel.ForEach(files, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, file =>
{
try
{
var input = new QrImageInput(
AnyBitmap.FromFile(file),
QrScanMode.OnlyDetectionModel);
// Per-thread QrReader instance — safe default
var results = new QrReader().Read(input);
foreach (QrResult result in results)
{
allResults.Add((Path.GetFileName(file), result.Value));
}
}
catch (Exception ex)
{
Interlocked.Increment(ref failCount);
Console.Error.WriteLine($"[ERROR] {Path.GetFileName(file)}: {ex.Message}");
}
});
sw.Stop();
Console.WriteLine($"Processed {files.Length} files in {sw.Elapsed.TotalSeconds:F1}s");
Console.WriteLine($"QR codes found: {allResults.Count} | Failures: {failCount}");
Console.WriteLine($"Throughput: {files.Length / sw.Elapsed.TotalSeconds:F1} files/sec");
Imports IronQr
Imports IronQr.Enum
Imports IronSoftware.Drawing
Imports System.Collections.Concurrent
Imports System.Diagnostics
Dim files As String() = Directory.GetFiles("qr-images/", "*.png")
Dim allResults As New ConcurrentBag(Of (File As String, Value As String))()
Dim failCount As Integer = 0
Dim sw As Stopwatch = Stopwatch.StartNew()
Parallel.ForEach(files, New ParallelOptions With {.MaxDegreeOfParallelism = Environment.ProcessorCount}, Sub(file)
Try
Dim input As New QrImageInput(AnyBitmap.FromFile(file), QrScanMode.OnlyDetectionModel)
' Per-thread QrReader instance — safe default
Dim results = New QrReader().Read(input)
For Each result As QrResult In results
allResults.Add((Path.GetFileName(file), result.Value))
Next
Catch ex As Exception
Interlocked.Increment(failCount)
Console.Error.WriteLine($"[ERROR] {Path.GetFileName(file)}: {ex.Message}")
End Try
End Sub)
sw.Stop()
Console.WriteLine($"Processed {files.Length} files in {sw.Elapsed.TotalSeconds:F1}s")
Console.WriteLine($"QR codes found: {allResults.Count} | Failures: {failCount}")
Console.WriteLine($"Throughput: {files.Length / sw.Elapsed.TotalSeconds:F1} files/sec")
Sortie
La console affiche un résumé du traitement par lots, incluant le nombre de fichiers traités, le temps de traitement, les codes QR trouvés, les éventuelles erreurs et le débit. Il liste ensuite chaque nom de fichier avec son URL décodée.
Téléchargez les 10 images d'entrée de code QR du lot de test (batch-qr-images.zip).
ConcurrentBag<t> rassemble les résultats de tous les threads sans nécessiter de verrous. Un compteur thread-safe suit les échecs, et l'utilisation de try-catch pour chaque fichier garantit qu'une image défectueuse n'interrompt pas le traitement par lots entier. Cette approche suit le modèle d'isolation des erreurs décrit dans le guide pratique de gestion des erreurs .
Définissez MaxDegreeOfParallelism à Environment.ProcessorCount pour correspondre au nombre de cœurs du processeur. L'utilisation de threads supplémentaires augmente la surcharge et n'améliore pas les performances, en particulier pour les modèles d'apprentissage automatique gourmands en ressources CPU.
Combinaison du traitement asynchrone et parallèle
Pour les pipelines à volume élevé, associez SemaphoreSlim avec Task.WhenAll pour limiter la concurrence. Contrairement à Parallel.ForEach, ce modèle maintient les E/S non bloquantes tout en contrôlant le nombre de décodages exécutés simultanément, évitant ainsi la saturation du pool de threads sous des charges de travail importantes.
Entrée
Quatre des vingt images de test de code QR ont été traitées par le pipeline concurrent. Chaque image encode une URL et est décodée en parallèle à l'aide d'une concurrence limitée via SemaphoreSlim.
Image 1 (Pipeline 1 sur 20)
Image 2 (Pipeline 2 sur 20)
Image 3 (Pipeline 3 sur 20)
Image 4 (Pipeline 4 sur 20)
:path=/static-assets/qr/content-code-examples/how-to/async-and-multithreading/semaphore-pipeline.cs
using IronQr;
using IronQr.Enum;
using IronSoftware.Drawing;
using System.Collections.Concurrent;
using System.Diagnostics;
string[] files = Directory.GetFiles("high-volume/", "*.png");
var results = new ConcurrentBag<(string File, string Value)>();
int maxConcurrency = Environment.ProcessorCount;
using var semaphore = new SemaphoreSlim(maxConcurrency);
var sw = Stopwatch.StartNew();
var tasks = files.Select(async file =>
{
await semaphore.WaitAsync();
try
{
var bmp = AnyBitmap.FromFile(file);
// OnlyDetectionModel: fastest per-image — critical at scale
var input = new QrImageInput(bmp, QrScanMode.OnlyDetectionModel);
var qrResults = await new QrReader().ReadAsync(input);
foreach (var qr in qrResults)
{
results.Add((Path.GetFileName(file), qr.Value));
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"{{\"file\":\"{Path.GetFileName(file)}\",\"error\":\"{ex.Message}\"}}");
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(tasks);
sw.Stop();
Console.WriteLine($"Pipeline complete: {results.Count} QR codes from {files.Length} files in {sw.Elapsed.TotalSeconds:F1}s");
Imports IronQr
Imports IronQr.Enum
Imports IronSoftware.Drawing
Imports System.Collections.Concurrent
Imports System.Diagnostics
Module Program
Sub Main()
Dim files As String() = Directory.GetFiles("high-volume/", "*.png")
Dim results As New ConcurrentBag(Of (File As String, Value As String))()
Dim maxConcurrency As Integer = Environment.ProcessorCount
Using semaphore As New SemaphoreSlim(maxConcurrency)
Dim sw As Stopwatch = Stopwatch.StartNew()
Dim tasks = files.Select(Function(file) Task.Run(Async Function()
Await semaphore.WaitAsync()
Try
Dim bmp = AnyBitmap.FromFile(file)
' OnlyDetectionModel: fastest per-image — critical at scale
Dim input As New QrImageInput(bmp, QrScanMode.OnlyDetectionModel)
Dim qrResults = Await (New QrReader()).ReadAsync(input)
For Each qr In qrResults
results.Add((Path.GetFileName(file), qr.Value))
Next
Catch ex As Exception
Console.Error.WriteLine($"{{""file"":""{Path.GetFileName(file)}"",""error"":""{ex.Message}""}}")
Finally
semaphore.Release()
End Try
End Function))
Task.WhenAll(tasks).Wait()
sw.Stop()
Console.WriteLine($"Pipeline complete: {results.Count} QR codes from {files.Length} files in {sw.Elapsed.TotalSeconds:F1}s")
End Using
End Sub
End Module
Sortie
La console affiche un résumé une fois le pipeline terminé : nombre total de codes QR décodés, nombre de fichiers sources et temps écoulé, suivis du nom de chaque fichier et de son URL décodée.
Téléchargez les 20 images d'entrée de code QR du pipeline de test (high-volume-qr-images.zip).
Adaptez la limite des sémaphores au nombre de cœurs disponibles pour optimiser le débit, ou réduisez-la lorsque la pression sur la mémoire est un problème avec les images volumineuses.
Lecture supplémentaire
- Exemple d'analyse ML : comparaison du mode d'analyse avec des exemples de code.
- Comment lire les codes QR : structure de l'entrée et modèles de lecture de base.
- Tutoriel sur le générateur de codes QR : génération avec mise en forme.
- Référence de l'API QrReader : signatures des méthodes et remarques.
- Référence de l'API QrWriter : toutes les surcharges d'écriture.
- Gestion des erreurs : mode d'emploi : isolation des erreurs par fichier et modèles de journalisation.
Voir les options de licence lorsque le pipeline est prêt pour la production.

