IronQR 操作指南 异步和多线程 如何在 C# 中使用异步和多线程进行二维码操作 Curtis Chau 已更新:2026年3月5日 下载 IronQR NuGet 下载 免费试用 LLM副本 LLM副本 将页面复制为 Markdown 格式,用于 LLMs 在 ChatGPT 中打开 向 ChatGPT 咨询此页面 在双子座打开 向 Gemini 询问此页面 在 Grok 中打开 向 Grok 询问此页面 打开困惑 向 Perplexity 询问有关此页面的信息 分享 在 Facebook 上分享 分享到 X(Twitter) 在 LinkedIn 上分享 复制链接 电子邮件文章 This article was translated from English: Does it need improvement? Translated View the article in English 单线程二维码扫描会在每次图像解码期间阻塞调用线程。 在 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 Free 30 Day Trial 最小工作流程(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) $vbLabelText $csharpLabel 输出 终端以 [QrType] Value 的格式显示解码后的二维码类型和值,然后确认 output-qr.png 已保存。 QrScanMode.Auto 运行 ML 检测和基本扫描过程,并在每个结果中填充解码值和 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") $vbLabelText $csharpLabel 输出 控制台显示批处理摘要,包括已处理的文件数、处理时间、找到的二维码、任何故障和吞吐量。 然后它会列出每个文件名及其解码后的 URL。 下载全部 10 个测试批次的二维码输入图像(batch-qr-images.zip)。 ConcurrentBag<t> 从所有线程收集结果,而无需加锁。 线程安全的计数器会跟踪故障,并且对每个文件使用 try-catch 可以确保一个错误的图像不会中断整个批次。 这种方法遵循错误处理指南中描述的错误隔离模式。 将 MaxDegreeOfParallelism 设置为 Environment.ProcessorCount,以与 CPU 核心数保持一致。 使用额外的线程会增加开销,并且不会提高性能,特别是对于 CPU 密集型机器学习模型而言。 结合异步和并行处理 对于高容量管道,将 SemaphoreSlim 与 Task.WhenAll 配对以限制并发性。 与 Parallel.ForEach 不同,这种模式保持 I/O 非阻塞,同时控制一次运行的解码数量,防止在大工作负载下线程池饱和。 输入 并发流水线处理的二十个二维码测试图像中的四个。每个图像编码一个 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 $vbLabelText $csharpLabel 输出 控制台会在管道完成后显示摘要:解码的二维码总数、源文件数量和经过的时间,以及每个文件名及其解码后的 URL。 下载全部 20 个测试流程 QR 码输入图像(high-volume-qr-images.zip)。 为了提高吞吐量,应使信号量限制与可用核心数相匹配;如果处理大型图像时内存压力较大,则应降低信号量限制。 进一步阅读 ML 扫描示例:扫描模式与代码示例的比较。 -如何读取二维码:输入结构和基本读取模式。 -二维码生成器教程:生成带样式的二维码。 QrReader API 参考:方法签名和备注。 QrWriter API 参考:所有写入重载。 -错误处理方法:按文件进行错误隔离和日志记录模式。 当管道准备就绪投入生产时,请查看许可选项。 Curtis Chau 立即与工程团队聊天 技术作家 Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。 准备开始了吗? Nuget 下载 63,625 | 版本: 2026.4 刚刚发布 免费试用 免费 NuGet 下载 总下载量:63,625 查看许可证 还在滚动吗? 想快速获得证据? PM > Install-Package IronQR 运行示例 观看您的 URL 变成 QR 代码。 免费 NuGet 下载 总下载量:63,625 查看许可证