QRコード操作で非同期とマルチスレッドをC&#35で使用する方法

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

単一スレッドのQRスキャンは、すべての画像デコードの間、呼び出しスレッドをブロックします。

WPFのボタンハンドラーでは、デコードが完了するまでUIがフリーズします。 数百枚の画像を処理するバッチ処理では、並列処理が可能なCPUコアがアイドル状態になってしまう。 IronQRのTask.WhenAllと連携して動作します。

このガイドでは、QRコードを非同期で処理する方法、バッチ読み取りをCPUコア全体に分散させる方法、および両方のパターンを組み合わせて大容量パイプラインを構築する方法について説明します。

クイックスタート: QRコードを非同期で処理する

画像を読み込み、呼び出し元のスレッドをブロックせずにデコード結果を待機します。

  1. IronQR をNuGetパッケージマネージャでインストール

    PM > Install-Package IronQR
  2. このコード スニペットをコピーして実行します。

    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. 実際の環境でテストするためにデプロイする

    今日プロジェクトで IronQR を使い始めましょう無料トライアル

    arrow pointer

QRコードを非同期で読み取る

ReadAsync は、待機可能なタスクを返すため、WPF/MAUI のイベントハンドラー、ASP.NET コントローラーのアクション、または任意の非同期メソッドと互換性があります。 入力は画像ビットマップから構築する必要があり、ファイルパスによる指定はできません。

書き込み側は同期処理であり、非同期処理のバリエーションはありません。 ファイル I/O 中にスレッドがブロックされるのを防ぐため、ビットマップからエクスポートされた生バイトデータを使用して、保存ステップを File.WriteAllBytesAsync() でラップしてください。

入力

非同期読み書きパターンを実証するために、QRコードイベントバッジをスキャンして再生成した。

QRコードエンコーディング(https://ironsoftware.com/event-badge)を非同期読み取り入力として使用
: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

出力

ターミナルには、デコードされたQRコードのタイプと値が [QrType] Value の形式で表示され、その後 output-qr.png が保存されたことが確認されます。

ターミナル出力には、[QRコード]のデコード値とoutput-qr.pngの保存確認が表示されます。

QrScanMode.Auto は、ML検出と基本スキャン処理の両方を実行し、各結果にデコードされた値とQRコードの種類を格納します。 OnlyDetectionModel は処理が高速ですが、バウンディングボックスの座標のみを返し、値フィールドは空のままになります。 エンコードされたコンテンツが必要な場合は、常に Auto を使用してください。


マルチスレッドによるQRコードの処理

独立してデコード可能な画像については、Parallel.ForEach が利用可能な CPU コア間で処理を分散させます。 IronQRは共有されたリーダーインスタンスに対して明示的なスレッドセーフ性を保証していないため、反復処理ごとに個別の QrReader インスタンスを使用することが安全なデフォルト設定となります。

入力

並列バッチスキャンで使用された10個のQRコードテスト画像のうち4個。 各画像にはURLがエンコードされており、実行時にqr-images/フォルダから読み込まれます。

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

出力

コンソールには、処理されたファイル数、処理時間、検出されたQRコード数、発生したエラー、スループットなどを含むバッチの概要が表示されます。 次に、各ファイル名とそのデコードされたURLを一覧表示します。

並列バッチ処理結果を示すターミナル出力:処理されたファイル数、検出されたQRコード数、エラー数、スループット、ファイル名ごとのデコードされたURL

テストバッチ用のQRコード入力画像10枚すべてをダウンロードしてください(batch-qr-images.zip)。

ConcurrentBag<t> は、ロックを必要とせずにすべてのスレッドから結果を集約します。 スレッドセーフなカウンターが障害を追跡し、各ファイルにtry-catchブロックを使用することで、1つの不良画像がバッチ処理全体を中断させないようにしています。 このアプローチは、エラー処理のハウツーで説明されているエラー分離パターンに従います。

CPUコア数に合わせて、MaxDegreeOfParallelismEnvironment.ProcessorCount に変更してください。 スレッド数を増やすとオーバーヘッドが増加するだけで、特にCPU負荷の高い機械学習モデルではパフォーマンスが向上しません。


非同期処理と並列処理の組み合わせ

処理量が多いパイプラインでは、SemaphoreSlimTask.WhenAll を組み合わせて、並行処理を制限してください。 Parallel.ForEachとは異なり、このパターンではI/Oをノンブロッキングに保ちつつ、一度に実行されるデコードの数を制御することで、大規模なワークロード下でのスレッドプールの飽和を防ぎます。

入力

並列パイプラインによって処理された20枚のQRコードテスト画像のうち4枚。各画像にはURLがエンコードされており、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

出力

パイプラインが完了すると、コンソールに概要が表示されます。表示されるのは、デコードされたQRコードの総数、ソースファイルの数、経過時間、そして各ファイル名とそのデコードされたURLです。

パイプラインの結果を示すターミナル出力: ファイル名ごとにデコードされたURLを含む20個のファイルからの20個のQRコード

テストパイプラインのQRコード入力画像20枚すべてをダウンロードしてください(high-volume-qr-images.zip)。

スループットを最大化するために、セマフォの制限値を利用可能なコア数に合わせるか、大きな画像でメモリ負荷が懸念される場合は、制限値を下げてください。


さらなる読み物

パイプラインが生産準備が整ったときのライセンスオプションを表示

よくある質問

C# での非同期 QR コード処理とは?

C# での非同期 QR コード処理により、メインスレッドをブロックせずに QR コード操作を行うことができます。IronQR の ReadAsync メソッドのような機能を使用して、パフォーマンスと応答性を向上させます。

マルチスレッド処理は QR コード処理をどのように改善できますか?

マルチスレッド処理は、複数の操作を並行して実行できるようにすることで、QR コード処理を大幅に向上させ、IronQR を使用してアプリケーションの効率を改善します。

IronQR における ReadAsync メソッドとは何ですか?

IronQR の ReadAsync メソッドは、QR コードデータを他のタスクを遅らせることなく処理できるようにする、非同期 QR コード読み取りを可能にします。

Parallel.ForEach は QR コード処理にどのように役立ちますか?

Parallel.ForEach は、複数の QR コードを同時に処理し、タスクを複数のスレッドに分配することで、IronQR との効率的な利用が可能となり、QR コード操作のスピードアップを図れます。

QR コード操作での SemaphoreSlim の役割は何ですか?

SemaphoreSlim は同時実行タスクの数を制限し、IronQR を使った QR コードの並列処理中にリソースを効果的に管理するのを助けます。

IronQR は制限付きパイプラインで QR コードを処理するために使用できますか?

はい、IronQR を使用して、Semaphoreslim のような構造を用いて並行性とリソースの割り当ても制御しながら、制限付きパイプラインで QR コードを処理できます。

非同期 QR コード操作のための IronQR の利点は何ですか?

非同期 QR コード操作のために IronQR を使用することで、アプリケーションの応答性が向上し、メインスレッドでのブロックが減少し、大量のQR コードデータを効率的に処理できます。

IronQR を使用して QR コード操作を並列実行することは可能ですか?

はい、IronQR は QR コード操作の並列処理をサポートしており、C# のマルチスレッド機能を活用して、より迅速かつ効率的な QR コード処理が可能です。

IronQR は C# アプリケーションでの QR コード処理をどのように向上させますか?

IronQR は、強力な非同期およびマルチスレッド機能を提供し、処理時間を短縮し、C# アプリケーションのスケーラビリティを向上させます。

C# のどの機能が QR コード処理のための IronQR の使用を補完しますか?

C# の機能である async/await、Parallel.ForEach、および SemaphoreSlim は IronQR を補完し、効率的で高性能な QR コード処理のためのフレームワークを提供します。

カーティス・チャウ
テクニカルライター

Curtis Chauは、カールトン大学でコンピュータサイエンスの学士号を取得し、Node.js、TypeScript、JavaScript、およびReactに精通したフロントエンド開発を専門としています。直感的で美しいユーザーインターフェースを作成することに情熱を持ち、Curtisは現代のフレームワークを用いた開発や、構造の良い視覚的に魅力的なマニュアルの作成を楽しんでいます。

開発以外にも、CurtisはIoT(Internet of Things)への強い関心を持ち、ハードウェアとソフトウェアの統合方法を模索しています。余暇には、ゲームをしたりDiscordボットを作成したりして、技術に対する愛情と創造性を組み合わせています。

準備はできましたか?
Nuget ダウンロード 67,270 | バージョン: 2026.5 just released
Still Scrolling Icon

まだスクロールしていますか?

すぐに証拠が欲しいですか? PM > Install-Package IronQR
サンプルを実行する URL が QR コードになるのを見る。