Jak używać asynchroniczności i wielowątkowości do operacji z kodami QR w języku C
Skanowanie kodów QR w trybie jednowątkowym blokuje wątek wywołujący na czas dekodowania każdego obrazu.
W procedurze obsługi przycisku WPF powoduje to zawieszenie interfejsu użytkownika do czasu zakończenia dekodowania. W zadaniu wsadowym przetwarzającym setki obrazów pozostawia rdzenie procesora bezczynne, podczas gdy mogłyby one pracować równolegle. Metoda ReadAsync w IronQR przenosi poszczególne odczyty do zadania typu awaitable, a standardowa metoda Read współpracuje z Parallel.ForEach i Task.WhenAll w celu zapewnienia przepustowości wsadowej.
W niniejszym przewodniku pokazano, jak przetwarzać kody QR asynchronicznie, rozdzielać odczyty partii między rdzenie procesora oraz łączyć oba wzorce w przypadku potoków o dużej przepustowości.
Szybki start: Asynchroniczne przetwarzanie kodów QR
Załaduj obraz i poczekaj na zdekodowane wyniki bez blokowania wątku wywołującego.
-
Install IronQR with NuGet Package Manager
PM > Install-Package IronQR -
Skopiuj i uruchom ten fragment kodu.
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); -
Wdrożenie do testowania w środowisku produkcyjnym
Rozpocznij używanie IronQR w swoim projekcie już dziś z darmową wersją próbną
Minimalny proces (5 kroków)
- Pobierz bibliotekę IronQR C# do asynchronicznego przetwarzania kodów QR
- Użyj
ReadAsyncdo nieblokujących odczytów pojedynczych - Użyj
Parallel.ForEachdo przetwarzania wsadowego obciążającego procesor - Połącz z
SemaphoreSlim,aby uzyskać potoki o ograniczonej współbieżności - Zbierz wyniki z
IEnumerablei wydrukuj zdekodowane wartości
Asynchroniczne odczytywanie kodów QR
ReadAsync zwraca zadanie typu awaitable, dzięki czemu jest kompatybilne z procedurami obsługi zdarzeń WPF/MAUI, akcjami kontrolera ASP.NET lub dowolną metodą asynchroniczną. Dane wejściowe muszą być utworzone z bitmapy obrazu; nie ma przeciążenia ścieżki pliku.
Strona pisania jest synchroniczna i nie ma wariantów asynchronicznych. Aby uniknąć blokowania wątku podczas operacji wejścia/wyjścia plików, należy otoczyć krok zapisywania w File.WriteAllBytesAsync() przy użyciu surowych bajtów wyeksportowanych z mapy bitowej.
Dane wejściowe
Identyfikator wydarzenia w postaci kodu QR zeskanowany i wygenerowany ponownie w celu zademonstrowania asynchronicznego wzorca odczytu i zapisu.
: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)
Wynik
Terminal wyświetla zdekodowany typ i wartość kodu QR w formacie [QrType] Value, a następnie potwierdza, że output-qr.png został zapisany.
QrScanMode.Auto uruchamia zarówno wykrywanie ML, jak i podstawowy skan, wypełniając odkodowaną wartość i typ QR w każdym wyniku. OnlyDetectionModel działa szybciej, ale zwraca tylko współrzędne prostokąta ograniczającego, pozostawiając pole wartości puste. Używaj Auto zawsze, gdy potrzebna jest zakodowana treść.
Przetwarzanie kodów QR z wykorzystaniem wielowątkowości
W przypadku obrazów, które można dekodować niezależnie, Parallel.ForEach rozdziela pracę między dostępne rdzenie procesora. Oddzielna instancja QrReader na każdą iterację jest bezpiecznym ustawieniem domyślnym, ponieważ IronQR nie zapewnia wyraźnej gwarancji bezpieczeństwa wątków dla współdzielonych instancji czytnika.
Dane wejściowe
Cztery z dziesięciu obrazów testowych kodów QR użytych w równoległym skanowaniu partii. Każdy obraz zawiera zakodowany adres URL i jest odczytywany z folderu qr-images/ w czasie wykonywania.
Zdjęcie 1 (partia 1 z 10)
Zdjęcie 2 (partia 2 z 10)
Zdjęcie 3 (partia 3 z 10)
Zdjęcie 4 (partia 4 z 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")
Wynik
Konsola wyświetla podsumowanie partii, w tym liczbę przetworzonych plików, czas przetwarzania, znalezione kody QR, ewentualne błędy oraz przepustowość. Następnie podaje każdą nazwę pliku wraz z jego zdekodowanym adresem URL.
Pobierz wszystkie 10 obrazów testowych do wprowadzania kodów QR (batch-qr-images.zip).
ConcurrentBag<t> gromadzi wyniki ze wszystkich wątków bez konieczności stosowania blokad. Licznik bezpieczny dla wątków śledzi awarie, a użycie try-catch dla każdego pliku gwarantuje, że jeden uszkodzony obraz nie przerwie całej partii. Podejście to jest zgodne z wzorcem izolacji błędów opisanym w poradniku dotyczącym obsługi błędów.
Ustaw MaxDegreeOfParallelism na Environment.ProcessorCount, aby dopasować do liczby rdzeni procesora. Użycie dodatkowych wątków zwiększa obciążenie i nie poprawia wydajności, szczególnie w przypadku modeli ML wymagających dużej mocy obliczeniowej procesora.
Łączenie przetwarzania asynchronicznego i równoległego
W przypadku potoków o dużej przepustowości należy połączyć SemaphoreSlim z Task.WhenAll, aby ograniczyć współbieżność. W przeciwieństwie do Parallel.ForEach, ten wzorzec zapewnia nieblokujące operacje wejścia/wyjścia, jednocześnie kontrolując liczbę dekodowań uruchamianych jednocześnie, co zapobiega nasyceniu puli wątków przy dużym obciążeniu.
Dane wejściowe
Cztery z dwudziestu testowych obrazów QR przetworzonych przez potok współbieżny. Każdy obraz koduje adres URL i jest dekodowany równolegle przy użyciu ograniczonej współbieżności za pomocą SemaphoreSlim.
Zdjęcie 1 (Potok 1 z 20)
Zdjęcie 2 (Pipeline 2 z 20)
Zdjęcie 3 (Pipeline 3 z 20)
Zdjęcie 4 (Pipeline 4 z 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
Wynik
Po zakończeniu działania potoku konsola wyświetla podsumowanie: łączną liczbę zdekodowanych kodów QR, liczbę plików źródłowych i czas, który upłynął, a następnie nazwę każdego pliku i jego zdekodowany adres URL.
Pobierz wszystkie 20 obrazów testowych z kodami QR (high-volume-qr-images.zip).
Dostosuj limit semaforów do dostępnej liczby rdzeni w celu uzyskania przepustowości lub zmniejsz go, gdy obciążenie pamięci stanowi problem w przypadku dużych obrazów.
Więcej informacji
- Przykład skanowania ML: porównanie trybów skanowania z przykładami kodu.
- Instrukcja odczytu kodów QR: budowa wejścia i podstawowe wzorce odczytu.
- Samouczek dotyczący generatora kodów QR: generowanie z zastosowaniem stylów.
- Dokumentacja API QrReader: sygnatury metod i uwagi.
- Dokumentacja API QrWriter: wszystkie nadładowania funkcji write.
- Poradnik obsługi błędów: izolacja błędów na poziomie plików i wzorce rejestrowania.
Zapoznaj się z opcjami licencyjnymi, gdy potok będzie gotowy do produkcji.
Często Zadawane Pytania
Czym jest asynchroniczne przetwarzanie kodu QR w C#?
Asynchroniczne przetwarzanie kodu QR w C# pozwala wykonywać operacje kodu QR bez blokowania głównego wątku, wykorzystując funkcje takie jak metoda ReadAsync IronQR, aby poprawić wydajność i responsywność.
Jak wielowątkowość może zwiększyć wydajność przetwarzania kodu QR?
Wielowątkowość może znacznie zwiększyć wydajność przetwarzania kodu QR, umożliwiając jednoczesne uruchamianie wielu operacji, co umożliwia szybsze czasy przetwarzania i poprawioną efektywność aplikacji z wykorzystaniem IronQR.
Czym jest metoda ReadAsync w IronQR?
Metoda ReadAsync w IronQR umożliwia asynchroniczne czytanie kodów QR, pozwalając twojej aplikacji C# obsługiwać dane kodu QR bez opóźniania innych zadań.
W jaki sposób Parallel.ForEach pomaga w przetwarzaniu kodów QR?
Parallel.ForEach umożliwia jednoczesne przetwarzanie wielu kodów QR przez rozdzielanie zadań na wiele wątków, które można efektywnie wykorzystać z IronQR, aby przyspieszyć operacje kodów QR.
Jaką rolę odgrywa SemaphoreSlim w operacjach kodu QR?
SemaphoreSlim jest używane do ograniczania liczby współbieżnych zadań, co pomaga efektywnie zarządzać zasobami podczas przetwarzania równoległego kodów QR z IronQR.
Czy można użyć IronQR do przetwarzania kodów QR w ograniczonym potoku?
Tak, IronQR można użyć do przetwarzania kodów QR w ograniczonym potoku, korzystając z konstrukcji takich jak SemaphoreSlim, aby skutecznie kontrolować współbieżność i alokację zasobów.
Jakie są korzyści z używania IronQR do asynchronicznych operacji kodu QR?
Użycie IronQR do operacji asynchronicznych kodu QR poprawia responsywność aplikacji, zmniejsza blokowanie na głównym wątku i pozwala na efektywne obsługiwanie dużych ilości danych kodu QR.
Czy można uruchamiać operacje kodu QR równolegle za pomocą IronQR?
Tak, IronQR obsługuje przetwarzanie równoległe operacji kodu QR, pozwalając wykorzystać możliwości wielowątkowości w C# do szybszej i bardziej wydajnej obsługi kodów QR.
W jaki sposób IronQR zwiększa przetwarzanie kodów QR w aplikacjach C#?
IronQR zwiększa przetwarzanie kodów QR poprzez dostarczanie solidnych możliwości asynchronicznych i wielowątkowych, co zmniejsza czas przetwarzania i poprawia skalowalność aplikacji C#.
Jakie funkcje C# uzupełniają użycie IronQR do przetwarzania kodów QR?
Funkcje C# takie jak async/await, Parallel.ForEach i SemaphoreSlim uzupełniają IronQR, zapewniając ramy dla efektywnego i wydajnego przetwarzania kodów QR.

