如何在 C# 中使用异步和多线程进行二维码操作
单线程二维码扫描会在每次图像解码期间阻塞调用线程。
在 WPF 按钮处理程序中,这将冻结 UI,直到解码完成。 在批量处理数百张图像时,CPU 核心会闲置,而它们本可以并行工作。 IronQR 的 ReadAsync 方法将单次读取操作卸载到可等待任务中,而标准的 Read 方法则配合 Parallel.ForEach 和 Task.WhenAll 实现批量吞吐。
本指南演示了如何异步处理二维码、将批量读取任务分配到 CPU 内核上,以及如何将这两种模式结合起来以实现高容量流水线。
快速入门:异步处理二维码
加载图像并等待解码结果,而不阻塞调用线程。
-
使用 NuGet 包管理器安装 https://www.nuget.org/packages/IronQR
PM > Install-Package IronQR -
复制并运行这段代码。
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); -
部署到您的生产环境中进行测试
通过免费试用立即在您的项目中开始使用IronQR
最小工作流程(5 个步骤)
- 下载 IronQR C# 库,实现异步二维码处理
- 使用
ReadAsync进行非阻塞式单次读取 - 使用
Parallel.ForEach进行 CPU 密集型批处理 - 与
SemaphoreSlim结合使用,构建有界并发管道 - 从
IEnumerable中收集结果并打印解码后的值
异步读取二维码
ReadAsync 返回一个可等待的任务,使其与 WPF/MAUI 事件处理程序、ASP.NET 控制器操作或任何异步方法兼容。 输入必须由图像位图构成;不支持文件路径重载。
写入端是同步的,没有异步变体。 为避免在文件 I/O 过程中阻塞线程,请使用从位图导出的原始字节,将保存步骤封装在 File.WriteAllBytesAsync() 中。
输入
扫描并重新生成二维码活动徽章,以演示异步读写模式。
: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)
输出
终端以 [QrType] Value 的格式显示解码后的 QR 类型和值,随后确认已保存 output-qr.png。
QrScanMode.Auto 同时运行机器学习检测和基础扫描流程,并在每个结果中填充解码值和QR类型。 OnlyDetectionModel 速度更快,但仅返回边界框坐标,导致值字段为空。 如需显示编码内容,请使用 Auto。
使用多线程处理二维码
对于可独立解码的图像,Parallel.ForEach 会将任务分配给可用的 CPU 核心。 每次迭代使用独立的 QrReader 实例是安全的默认做法,因为 IronQR 并未对共享的读取器实例提供明确的线程安全保证。
输入
并行批量扫描中使用的十个二维码测试图像中有四个。 每张图片都编码了一个 URL,并在运行时从 qr-images/ 文件夹中读取。
图片 1(第 1 批,共 10 批)
图片 2(第 2 批,共 10 张)
图片 3(第 3 批,共 10 批)
图片 4(共 10 张,第 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")
输出
控制台显示批处理摘要,包括已处理的文件数、处理时间、找到的二维码、任何故障和吞吐量。 然后它会列出每个文件名及其解码后的 URL。
下载全部 10 个测试批次的二维码输入图像(batch-qr-images.zip)。
ConcurrentBag<t> 可从所有线程中收集结果,且无需使用锁。 线程安全的计数器会跟踪故障,并且对每个文件使用 try-catch 可以确保一个错误的图像不会中断整个批次。 这种方法遵循错误处理指南中描述的错误隔离模式。
将 MaxDegreeOfParallelism 设置为 Environment.ProcessorCount,以匹配 CPU 核心数。 使用额外的线程会增加开销,并且不会提高性能,特别是对于 CPU 密集型机器学习模型而言。
结合异步和并行处理
对于高吞吐量的管道,请将 SemaphoreSlim 与 Task.WhenAll 配合使用以限制并发。 与 Parallel.ForEach 不同,此模式在保持 I/O 非阻塞的同时,还能控制同时运行的解码任务数量,从而防止在高负载情况下线程池饱和。
输入
并发管道处理的二十张 QR 测试图像中的四张。每张图像都编码了一个 URL,并通过 SemaphoreSlim 采用有界并发方式进行并行解码。
图 1(共 20 道流程中的第 1 道)
图 2(20 个流程中的第 2 个)
图 3(共 20 道流程中的第 3 道)
图 4(共 20 道流程中的第 4 道)
: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
输出
控制台会在管道完成后显示摘要:解码的二维码总数、源文件数量和经过的时间,以及每个文件名及其解码后的 URL。
下载全部 20 个测试流程 QR 码输入图像(high-volume-qr-images.zip)。
为了提高吞吐量,应使信号量限制与可用核心数相匹配;如果处理大型图像时内存压力较大,则应降低信号量限制。
进一步阅读
- ML 扫描示例:扫描模式与代码示例的比较。 -如何读取二维码:输入结构和基本读取模式。 -二维码生成器教程:生成带样式的二维码。
- QrReader API 参考:方法签名和备注。
- QrWriter API 参考:所有写入重载。 -错误处理方法:按文件进行错误隔离和日志记录模式。
当管道准备就绪投入生产时,请查看许可选项。
常见问题解答
什么是C#中异步QR码处理?
C#中的异步QR码处理允许您执行QR码操作而不会阻塞主线程,使用像IronQR的ReadAsync方法这样的功能来提升性能和响应性。
多线程如何提高QR码的处理效率?
多线程可以显著提高QR码的处理效率,通过允许多项操作并发进行,利用IronQR可促进更快的处理时间和改进的应用效率。
IronQR中的ReadAsync方法是什么?
IronQR中的ReadAsync方法使QR码的异步读取成为可能,允许您的C#应用程序处理QR码数据而不会延误其他任务。
Parallel.ForEach在QR码处理中有何帮助?
Parallel.ForEach允许通过将任务分配到多个线程同时处理多个QR码,这可以与IronQR一起有效利用以加快QR码操作速度。
SemaphoreSlim在QR码操作中起什么作用?
SemaphoreSlim用于限制并发任务的数量,帮助在使用IronQR进行QR码的并行处理中有效管理资源。
IronQR是否可用于在有界管道中处理QR码?
是的,IronQR可用于在有界管道中处理QR码,使用如SemaphoreSlim的构造来有效地控制并发和资源分配。
使用IronQR进行异步QR码操作有哪些好处?
使用IronQR进行异步QR码操作改善了应用响应性,减少了主线程的阻塞,并允许高效处理大量的QR码数据。
是否可以使用IronQR并行运行QR码操作?
是的,IronQR支持并行处理QR码操作,允许您在C#中利用多线程能力快速且高效地处理QR码。
IronQR如何增强C#应用程序中的QR码处理?
IronQR通过提供强大的异步和多线程能力来增强QR码处理,减少处理时间并提高C#应用程序的可扩展性。
哪些C#功能可以补充IronQR在QR码处理中的使用?
C#特性如async/await、Parallel.ForEach和SemaphoreSlim补充了IronQR,提供了高效高性能的QR码处理框架。

