IronQR 操作指南 非同步和多執行緒 如何在 C# 中使用 Async 和多執行緒進行 QR code 掃描與處理 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 單執行緒 QR 碼掃描會在每次影像解碼期間阻塞呼叫執行緒。 在 WPF 按鈕處理程序中,這將凍結 UI,直到解碼完成。 在批量處理數百張圖像時,CPU 核心會閒置,而它們本可以並行工作。 IronQR 的 ReadAsync 方法將單一讀取操作卸載到可等待的任務中,而標準的 Read 方法與 Parallel.ForEach 和 Task.WhenAll 使用,以實現批量匹配。 本指南示範如何非同步處理二維碼、將批次讀取任務分配到 CPU 核心上,以及如何將這兩種模式結合起來以實現高容量管線。 快速入門:異步處理 QR 碼 載入圖像並等待解碼結果,而不阻塞呼叫線程。 使用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# 程式庫,以進行非同步 QR 碼處理 使用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 熱衷於創建直觀且美觀的用戶界面,喜歡使用現代框架並打造結構良好、視覺吸引人的手冊。除了開發之外,Curtis 對物聯網 (IoT) 有著濃厚的興趣,探索將硬體和軟體結合的創新方式。在閒暇時間,他喜愛遊戲並構建 Discord 機器人,結合科技與創意的樂趣。 準備好開始了嗎? Nuget 下載 63,625 | 版本: 2026.4 剛剛發布 開始免費試用 免費 NuGet 下載 總下載量:63,625 查看許可證 還在捲動嗎? 想要快速證明? PM > Install-Package IronQR 執行範例 觀看您的 URL 變成 QR code。 免費 NuGet 下載 總下載量:63,625 查看許可證