Jak używać asynchroniczności i wielowątkowości do operacji z kodami QR w języku C

This article was translated from English: Does it need improvement?
Translated
View the article in English

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.

  1. Install IronQR with NuGet Package Manager

    PM > Install-Package IronQR
  2. 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);
  3. Wdrożenie do testowania w środowisku produkcyjnym

    Rozpocznij używanie IronQR w swoim projekcie już dziś z darmową wersją próbną

    arrow pointer

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.

Kodowanie kodu QR https://ironsoftware.com/event-badge używane jako asynchroniczne odczytywanie danych wejściowych
: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)
$vbLabelText   $csharpLabel

Wynik

Terminal wyświetla zdekodowany typ i wartość kodu QR w formacie [QrType] Value, a następnie potwierdza, że output-qr.png został zapisany.

Wynik na terminalu pokazujący zdekodowaną wartość [QR] oraz potwierdzenie zapisania pliku output-qr.png

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.

Batch QR code input image 1 of 10 encoding https://ironsoftware.com/batch-1
Batch QR code input image 2 of 10 encoding https://ironsoftware.com/batch-2
Batch QR code input image 3 of 10 encoding https://ironsoftware.com/batch-3
Batch QR code input image 4 of 10 encoding https://ironsoftware.com/batch-4
: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")
$vbLabelText   $csharpLabel

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.

Wynik terminala pokazujący równoległe wyniki partii: 10 przetworzonych plików, znalezione kody QR, błędy, przepustowość i zdekodowany adres URL na nazwę pliku

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.

Pipeline QR code input image 1 of 20
Pipeline QR code input image 2 of 20
Pipeline QR code input image 3 of 20
Pipeline QR code input image 4 of 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
$vbLabelText   $csharpLabel

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.

Wynik działania programu wyświetlany w terminalu: 20 kodów QR z 20 plików z odkodowanym adresem URL dla każdego pliku

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

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.

A PHP Error was encountered

Severity: Warning

Message: Illegal string offset 'name'

Filename: sections/author_component.php

Line Number: 18

Backtrace:

File: /var/www/ironpdf.com/application/views/main/sections/author_component.php
Line: 18
Function: _error_handler

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 63
Function: view

File: /var/www/ironpdf.com/application/views/products/sections/three_column_docs_page_structure.php
Line: 64
Function: main_view

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 88
Function: view

File: /var/www/ironpdf.com/application/views/products/how-to/index.php
Line: 2
Function: view

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 88
Function: view

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 552
Function: view

File: /var/www/ironpdf.com/application/controllers/Products/Howto.php
Line: 31
Function: render_products_view

File: /var/www/ironpdf.com/index.php
Line: 292
Function: require_once

k

A PHP Error was encountered

Severity: Warning

Message: Illegal string offset 'title'

Filename: sections/author_component.php

Line Number: 38

Backtrace:

File: /var/www/ironpdf.com/application/views/main/sections/author_component.php
Line: 38
Function: _error_handler

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 63
Function: view

File: /var/www/ironpdf.com/application/views/products/sections/three_column_docs_page_structure.php
Line: 64
Function: main_view

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 88
Function: view

File: /var/www/ironpdf.com/application/views/products/how-to/index.php
Line: 2
Function: view

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 88
Function: view

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 552
Function: view

File: /var/www/ironpdf.com/application/controllers/Products/Howto.php
Line: 31
Function: render_products_view

File: /var/www/ironpdf.com/index.php
Line: 292
Function: require_once

k

A PHP Error was encountered

Severity: Warning

Message: Illegal string offset 'comment'

Filename: sections/author_component.php

Line Number: 48

Backtrace:

File: /var/www/ironpdf.com/application/views/main/sections/author_component.php
Line: 48
Function: _error_handler

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 63
Function: view

File: /var/www/ironpdf.com/application/views/products/sections/three_column_docs_page_structure.php
Line: 64
Function: main_view

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 88
Function: view

File: /var/www/ironpdf.com/application/views/products/how-to/index.php
Line: 2
Function: view

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 88
Function: view

File: /var/www/ironpdf.com/application/libraries/Render.php
Line: 552
Function: view

File: /var/www/ironpdf.com/application/controllers/Products/Howto.php
Line: 31
Function: render_products_view

File: /var/www/ironpdf.com/index.php
Line: 292
Function: require_once

k
Gotowy, aby rozpocząć?
Nuget Pliki do pobrania 67,270 | Wersja: 2026.5 just released
Still Scrolling Icon

Wciąż przewijasz?

Czy chcesz szybko dowodu? PM > Install-Package IronQR
uruchom próbkę obserwuj, jak Twój URL staje się kodem QR.