QR Code lesen mit asynchronen und Multithreading-Verfahren in C
Das Scannen von QR-Codes mit nur einem Thread blockiert den aufrufenden Thread für die Dauer jedes Bilddekodierungsvorgangs.
In einem WPF-Button-Handler friert dies die Benutzeroberfläche ein, bis die Dekodierung abgeschlossen ist. Bei der Verarbeitung von Hunderten von Bildern in einem Batch-Job bleiben CPU-Kerne ungenutzt, anstatt parallel zu arbeiten. Die Methode ReadAsync von IronQR lagert einzelne Lesevorgänge an eine awaitable Aufgabe aus, und die Standardmethode Read arbeitet mit Parallel.ForEach und Task.WhenAll für den Batch-Durchsatz zusammen.
Dieser Leitfaden zeigt, wie man QR-Codes asynchron verarbeitet, Batch-Lesevorgänge auf die CPU-Kerne verteilt und beide Vorgehensweisen für Pipelines mit hohem Datenvolumen kombiniert.
Schnellstart: QR-Codes asynchron verarbeiten
Laden Sie ein Bild und warten Sie auf die Dekodierungsergebnisse, ohne den aufrufenden Thread zu blockieren.
-
Installieren Sie IronQR mit NuGet Package Manager
PM > Install-Package IronQR -
Kopieren Sie diesen Codeausschnitt und führen Sie ihn aus.
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); -
Bereitstellen zum Testen in Ihrer Live-Umgebung
Beginnen Sie noch heute, IronQR in Ihrem Projekt zu verwenden, mit einer kostenlosen Testversion
Minimaler Arbeitsablauf (5 Schritte)
- Laden Sie die IronQR C#-Bibliothek für die asynchrone QR-Code-Verarbeitung herunter.
- Verwenden Sie
ReadAsyncfür nicht blockierende Einzellesevorgänge. - Verwenden Sie
Parallel.ForEachfür die CPU-intensive Stapelverarbeitung. - Kombinieren Sie dies mit
SemaphoreSlimfür Pipelines mit begrenzter Parallelität. - Sammle die Ergebnisse von
IEnumerableund gib die dekodierten Werte aus.
Asynchrones Lesen von QR-Codes
ReadAsync gibt eine awaitable Aufgabe zurück und ist somit kompatibel mit WPF/MAUI-Ereignisbehandlern, ASP.NET -Controller-Aktionen oder beliebigen asynchronen Methoden. Die Eingabe muss aus einer Bildbitmap erstellt werden; eine Überladung mit Dateipfaden ist nicht möglich.
Die Schreibseite ist synchron und verfügt über keine asynchronen Varianten. Um zu vermeiden, dass der Thread während der Datei-E/A blockiert wird, umschließen Sie den Speicherschritt mit File.WriteAllBytesAsync() unter Verwendung der aus der Bitmap exportierten Rohbytes.
Eingabe
Ein QR-Code-Event-Badge wurde gescannt und neu generiert, um das asynchrone Lese- und Schreibmuster zu demonstrieren.
: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)
Ausgabe
Das Terminal zeigt den dekodierten QR-Typ und -Wert im Format [QrType] Value an und bestätigt anschließend, dass output-qr.png gespeichert wurde.
QrScanMode.Auto führt sowohl eine ML-Erkennung als auch einen einfachen Scanvorgang durch und trägt den dekodierten Wert und den QR-Typ in jedes Ergebnis ein. OnlyDetectionModel ist schneller, gibt aber nur die Koordinaten des Begrenzungsrahmens zurück, das Wertfeld bleibt leer. Verwenden Sie Auto, wenn der codierte Inhalt benötigt wird.
Verarbeitung von QR-Codes mit Multithreading
Bei Bildern, die unabhängig dekodiert werden können, verteilt Parallel.ForEach die Arbeit auf die verfügbaren CPU-Kerne. Eine separate QrReader Instanz pro Iteration ist der sichere Standard, da IronQR keine explizite Thread-Sicherheitsgarantie für gemeinsam genutzte Reader-Instanzen gibt.
Eingabe
Vier der zehn QR-Code-Testbilder, die beim parallelen Stapelscan verwendet wurden. Jedes Bild kodiert eine URL und wird zur Laufzeit aus dem Ordner qr-images/ gelesen.
Bild 1 (Charge 1 von 10)
Bild 2 (Charge 2 von 10)
Bild 3 (Charge 3 von 10)
Bild 4 (Charge 4 von 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")
Ausgabe
Die Konsole zeigt eine Zusammenfassung des Batch-Verarbeitungsvorgangs an, einschließlich der Anzahl der verarbeiteten Dateien, der Verarbeitungszeit, der gefundenen QR-Codes, etwaiger Fehler und des Durchsatzes. Anschließend werden alle Dateinamen mit ihren dekodierten URLs aufgelistet.
Laden Sie alle 10 Test-QR-Code-Eingabebilder herunter (batch-qr-images.zip).
ConcurrentBag<t> sammelt Ergebnisse aus allen Threads, ohne dass Sperren erforderlich sind. Ein threadsicherer Zähler verfolgt Fehler, und die Verwendung von try-catch für jede Datei stellt sicher, dass ein fehlerhaftes Bild nicht den gesamten Stapel unterbricht. Dieser Ansatz folgt dem im Leitfaden zur Fehlerbehandlung beschriebenen Fehlerisolierungsmuster.
Setzen Sie MaxDegreeOfParallelism auf Environment.ProcessorCount, um die Werte an die Anzahl der CPU-Kerne anzupassen. Die Verwendung zusätzlicher Threads erhöht den Overhead und verbessert die Leistung nicht, insbesondere bei CPU-intensiven ML-Modellen.
Kombination von asynchroner und paralleler Verarbeitung
Für Pipelines mit hohem Durchsatz sollten Sie SemaphoreSlim mit Task.WhenAll kombinieren, um die Parallelität zu begrenzen. Im Gegensatz zu Parallel.ForEach sorgt dieses Muster dafür, dass die E/A nicht blockierend ist und gleichzeitig kontrolliert wird, wie viele Dekodierungen gleichzeitig ausgeführt werden, wodurch eine Überlastung des Thread-Pools bei großen Arbeitslasten verhindert wird.
Eingabe
Vier der zwanzig QR-Code-Testbilder wurden von der parallelen Verarbeitungspipeline verarbeitet. Jedes Bild kodiert eine URL und wird parallel mittels begrenzter Parallelität über SemaphoreSlim dekodiert.
Abbildung 1 (Pipeline 1 von 20)
Bild 2 (Pipeline 2 von 20)
Abbildung 3 (Pipeline 3 von 20)
Abbildung 4 (Pipeline 4 von 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
Ausgabe
Die Konsole zeigt nach Abschluss der Pipeline eine Zusammenfassung an: Gesamtzahl der dekodierten QR-Codes, Anzahl der Quelldateien und verstrichene Zeit, gefolgt von jedem Dateinamen und seiner dekodierten URL.
Laden Sie alle 20 QR-Code-Eingabebilder der Testpipeline herunter (high-volume-qr-images.zip).
Passen Sie das Semaphorlimit an die Anzahl der verfügbaren Kerne an, um einen hohen Durchsatz zu erzielen, oder verringern Sie es, wenn der Speicherdruck bei großen Images ein Problem darstellt.
Weiterführende Literatur
- Beispiel für ML-Scanning : Vergleich des Scanmodus mit Codebeispielen.
- Anleitung zum Lesen von QR-Codes : Eingabeaufbau und grundlegende Lesemuster.
- QR-Code-Generator-Tutorial : Generierung mit Styling.
- QrReader API-Referenz : Methodensignaturen und Anmerkungen.
- QrWriter API-Referenz : alle Schreibüberladungen.
- Anleitung zur Fehlerbehandlung : Fehlerisolierung und Protokollierungsmuster pro Datei.
Die Lizenzierungsoptionen können Sie einsehen , sobald die Pipeline produktionsbereit ist.

