Como Usar Async e Multithreading para Operações de Código QR em C

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

A digitalização de QR de um único thread bloqueia o thread de chamada durante a duração de cada decodificação de imagem.

Em um manipulador de botão WPF, isso congela a interface do usuário até que a decodificação seja concluída. Em um processamento em lote de centenas de imagens, isso deixa núcleos da CPU ociosos quando poderiam estar trabalhando em paralelo. O método ReadAsync do IronQR descarrega leituras individuais para uma tarefa aguardável, e o método padrão Read funciona com Parallel.ForEach e Task.WhenAll para processamento em lote.

Este guia demonstra como processar códigos QR de forma assíncrona, distribuir leituras em lote entre os núcleos da CPU e combinar ambos os padrões para pipelines de alto volume.

Início rápido: Processar Códigos QR Assincronamente

Carrega uma imagem e aguarda os resultados decodificados sem bloquear a thread de chamada.

  1. Instale IronQR com o Gerenciador de Pacotes NuGet

    PM > Install-Package IronQR
  2. Copie e execute este trecho de código.

    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. Implante para testar em seu ambiente de produção.

    Comece a usar IronQR em seu projeto hoje com uma avaliação gratuita

    arrow pointer

Leitura assíncrona de códigos QR

ReadAsync retorna uma tarefa assíncrona, tornando-a compatível com manipuladores de eventos WPF/MAUI, ações de controladores ASP.NET ou qualquer método assíncrono. A entrada deve ser construída a partir de um bitmap de imagem; não há sobrecarga de caminho de arquivo.

O lado da escrita é síncrono e não possui variantes assíncronas. Para evitar o bloqueio da thread durante a E/S de arquivos, envolva a etapa de salvamento em File.WriteAllBytesAsync() usando os bytes brutos exportados do bitmap.

Entrada

Um crachá de evento com código QR foi escaneado e regenerado para demonstrar o padrão de leitura e gravação assíncrona.

Codificação de código QR https://ironsoftware.com/event-badge usada como entrada de leitura assíncrona
: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);
$vbLabelText   $csharpLabel

Saída

O terminal exibe o tipo e o valor do QR decodificado no formato [QrType] Value e, em seguida, confirma que output-qr.png foi salvo.

Saída do terminal mostrando o valor decodificado do [QRCode] e a confirmação de salvamento em output-qr.png.

QrScanMode.Auto executa tanto a detecção por aprendizado de máquina quanto uma varredura básica, preenchendo o valor decodificado e o tipo de QR Code em cada resultado. OnlyDetectionModel é mais rápido, mas retorna apenas as coordenadas da caixa delimitadora, deixando o campo de valor vazio. Use Auto sempre que o conteúdo codificado for necessário.


Processamento de códigos QR com multithreading

Para imagens que podem ser decodificadas independentemente, Parallel.ForEach distribui o trabalho entre os núcleos de CPU disponíveis. Uma instância QrReader separada por iteração é o padrão seguro, já que o IronQR não oferece nenhuma garantia explícita de segurança de thread para instâncias de leitor compartilhadas.

Entrada

Quatro das dez imagens de teste de código QR usadas na leitura em lote paralela. Cada imagem codifica um URL e é lida da pasta qr-images/ em tempo de execução.

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");
$vbLabelText   $csharpLabel

Saída

O console exibe um resumo do lote, incluindo o número de arquivos processados, o tempo de processamento, os códigos QR encontrados, quaisquer falhas e a taxa de transferência. Em seguida, lista cada nome de arquivo com seu URL decodificado.

Saída do terminal mostrando os resultados do processamento em lote paralelo: 10 arquivos processados, códigos QR encontrados, falhas, taxa de transferência e URL decodificada por nome de arquivo.

Baixe todas as 10 imagens de teste para entrada de código QR em lote (batch-qr-images.zip).

ConcurrentBag<t> reúne resultados de todas as threads sem exigir bloqueios. Um contador thread-safe rastreia falhas, e o uso de try-catch para cada arquivo garante que uma imagem defeituosa não interrompa todo o lote. Essa abordagem segue o padrão de isolamento de erros descrito no guia de tratamento de erros .

Defina MaxDegreeOfParallelism para Environment.ProcessorCount para alinhar com o número de núcleos da CPU. O uso de threads adicionais aumenta a sobrecarga e não melhora o desempenho, especialmente para modelos de aprendizado de máquina que exigem muito da CPU.


Combinando processamento assíncrono e paralelo

Para pipelines de alto volume, combine SemaphoreSlim com Task.WhenAll para limitar a concorrência. Ao contrário de Parallel.ForEach, este padrão mantém a E/S não bloqueante, controlando ao mesmo tempo quantas decodificações são executadas simultaneamente, evitando a saturação do pool de threads sob grandes cargas de trabalho.

Entrada

Quatro das vinte imagens de teste de código QR processadas pelo pipeline concorrente. Cada imagem codifica um URL e é decodificada em paralelo usando concorrência limitada via 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");
$vbLabelText   $csharpLabel

Saída

O console exibe um resumo quando o pipeline termina: total de códigos QR decodificados, número de arquivos de origem e tempo decorrido, seguido por cada nome de arquivo e seu URL decodificado.

Saída do terminal mostrando os resultados do pipeline: 20 códigos QR de 20 arquivos com URL decodificada por nome de arquivo.

Baixe todas as 20 imagens de entrada de código QR do pipeline de teste (high-volume-qr-images.zip).

Ajuste o limite de semáforos à quantidade de núcleos disponíveis para obter a melhor taxa de transferência ou reduza-o quando a pressão sobre a memória for uma preocupação com imagens grandes.


Leitura complementar

Visualizar opções de licenciamento quando o pipeline estiver pronto para produção.

Curtis Chau
Redator Técnico

Curtis Chau é bacharel em Ciência da Computação (Universidade Carleton) e se especializa em desenvolvimento front-end, com experiência em Node.js, TypeScript, JavaScript e React. Apaixonado por criar interfaces de usuário intuitivas e esteticamente agradáveis, Curtis gosta de trabalhar com frameworks modernos e criar manuais ...

Leia mais
Pronto para começar?
Nuget Downloads 62,157 | Versão: 2026.3 acaba de ser lançado
Still Scrolling Icon

Ainda está rolando a tela?

Quer provas rápidas? PM > Install-Package IronQR
executar um exemplo Veja seu URL se transformar em um código QR.