# C#에서 QR 코드 작업에 대해 Async 및 멀티 스레드를 사용하는 방법
단일 스레드 QR 스캔은 매 이미지 디코드에 대해 호출 스레드를 차단합니다.
WPF 버튼 핸들러에서 이로 인해 디코딩이 완료될 때까지 UI가 정지됩니다. 수백 개의 이미지를 처리하는 일괄 작업에서 CPU 코어는 병렬로 작업할 수 있음에도 불구하고 유휴 상태로 남게 됩니다. IronQR의 `ReadAsync` 메서드는 개별 읽기를 대기 가능한 작업으로 분산하며, 표준 `Read` 메서드는 배치 처리량에 대해 `Parallel.ForEach` 및 `Task.WhenAll`와 함께 작동합니다.
이 가이드는 QR 코드를 비동기적으로 처리하는 방법, 배치 읽기 작업을 CPU 코어에 분산하는 방법, 그리고 대용량 파이프라인을 위해 두 가지 방식을 결합하는 방법을 보여줍니다.
*as-heading:2(빠른 시작: QR 코드 비동기 처리)*
이미지를 로드하고 호출 스레드를 차단하지 않고 디코딩된 결과를 기다립니다.
```csharp
:title=Quickstart
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);
```
<div class="hsg-featured-snippet">
<h3>최소 워크플로우(5단계)</h3>
<ol>
<li><a class="js-modal-open" data-modal-id="trial-license-after-download" href="https://nuget.org/packages/IronQR/">비동기 QR 코드 처리를 위한 IronQR C# 라이브러리 다운로드</a></li>
<li>비차단 단일 읽기에는 <code>ReadAsync</code> 사용하십시오.</li>
<li><code>Parallel.ForEach</code>를 사용하여 CPU 중심의 배치 처리 수행</li>
<li><code>SemaphoreSlim</code>을 결합하여 제한된 병행 처리 파이프라인</li>
<li><code>IEnumerable<QrResult></code> 에서 결과를 수집하고 디코딩된 값을 출력합니다.</li>
</ol>
</div>
## QR 코드 비동기 읽기
`ReadAsync`는 대기 가능한 작업을 반환하여, WPF/MAUI 이벤트 핸들러, ASP.NET 컨트롤러 작업, 또는 어떠한 비동기 메서드와도 호환되도록 합니다. 입력은 이미지 비트맵으로 구성되어야 하며, 파일 경로에 대한 오버로드는 없습니다.
쓰기 작업은 동기식이며 비동기식 변형은 없습니다. 파일 I/O 중 스레드를 차단하지 않으려면, 비트맵에서 내보내진 원시 바이트를 사용하여 `File.WriteAllBytesAsync()`에 저장 단계를 래핑하십시오.
### 입력
QR 코드 이벤트 배지를 스캔하고 다시 생성하여 비동기 읽기/쓰기 패턴을 시연합니다.
<div class="content-img-align-center">
<div class="center-image-wrapper" style="max-width: 300px;">
<img src="/static-assets/qr/how-to/async-and-multithreading/input-async-event-badge.webp"
alt="QR 코드 인코딩(https://ironsoftware.com/event-badge)은 비동기 읽기 입력으로 사용됩니다."
class="img-responsive add-shadow" />
</div>
</div>
```cs
:path=/static-assets/qr/content-code-examples/how-to/async-and-multithreading/async-read-write.cs
```
### 산출
터미널은 디코드된 QR 유형 및 값을 `[QrType] Value` 형식으로 표시하고 `output-qr.png`이 저장되었다는 것을 확인합니다.
<div class="content-img-align-center">
<div class="center-image-wrapper">
<img src="/static-assets/qr/how-to/async-and-multithreading/test1-async-out.webp" alt="터미널 출력 화면에 [QRCode] 디코딩 값과 output-qr.png 저장 확인 메시지가 표시됩니다." class="img-responsive add-shadow" />
</div>
</div>
`QrScanMode.Auto`는 ML 감지 및 기본 스캔 패스를 모두 실행하여 각 결과에 디코드된 값과 QR 유형을 채웁니다. `OnlyDetectionModel`는 더 빠르지만 바운딩 박스 좌표만 반환하며, 값 필드는 비어 있습니다. 인코딩된 내용이 필요할 때는 `Auto`를 사용하십시오.
---
## 멀티스레딩을 이용한 QR 코드 처리
독립적으로 디코드할 수 있는 이미지의 경우, `Parallel.ForEach`는 사용 가능한 CPU 코어 전체에 작업을 분산시킵니다. 각 반복마다 별도의 `QrReader` 인스턴스가 안전한 기본값입니다. 이는 IronQR이 공유된 판독기 인스턴스에 대해 명시적인 스레드 안정성 보장을 하지 않기 때문입니다.
### 입력
병렬 배치 스캔에 사용된 10개의 QR 코드 테스트 이미지 중 4개입니다. 각 이미지는 URL을 인코딩하고 런타임에 `qr-images/` 폴더에서 읽어옵니다.
<div class="competitors-section__wrapper-even-1">
<div class="competitors__card" style="width: 47%;">
<img src="/static-assets/qr/how-to/async-and-multithreading/input-batch-qr-1.webp"
alt="https://ironsoftware.com/batch-1을 인코딩한 배치 QR 코드 입력 이미지 10개 중 1번"
class="img-responsive add-shadow" style="max-width: 200px;" />
<p class="competitors__download-link" style="color: #181818; font-style: italic;">
이미지 1 (10개 중 1번째 배치)
</p>
</div>
<div class="competitors__card" style="width: 47%;">
<img src="/static-assets/qr/how-to/async-and-multithreading/input-batch-qr-2.webp"
alt="https://ironsoftware.com/batch-2를 인코딩한 배치 QR 코드 입력 이미지 10개 중 2번"
class="img-responsive add-shadow" style="max-width: 200px;" />
<p class="competitors__download-link" style="color: #181818; font-style: italic;">
이미지 2 (10개 중 2번째 배치)
</p>
</div>
</div>
<div class="competitors-section__wrapper-even-1">
<div class="competitors__card" style="width: 47%;">
<img src="/static-assets/qr/how-to/async-and-multithreading/input-batch-qr-3.webp"
alt="https://ironsoftware.com/batch-3을 인코딩한 배치 QR 코드 입력 이미지 10개 중 3번"
class="img-responsive add-shadow" style="max-width: 200px;" />
<p class="competitors__download-link" style="color: #181818; font-style: italic;">
이미지 3 (10개 중 3번째 배치)
</p>
</div>
<div class="competitors__card" style="width: 47%;">
<img src="/static-assets/qr/how-to/async-and-multithreading/input-batch-qr-4.webp"
alt="https://ironsoftware.com/batch-4를 인코딩한 배치 QR 코드 입력 이미지 10개 중 4번"
class="img-responsive add-shadow" style="max-width: 200px;" />
<p class="competitors__download-link" style="color: #181818; font-style: italic;">
이미지 4 (10개 중 4번째 배치)
</p>
</div>
</div>
```cs
:path=/static-assets/qr/content-code-examples/how-to/async-and-multithreading/parallel-batch.cs
```
### 산출
콘솔에는 처리된 파일 수, 처리 시간, 발견된 QR 코드 수, 오류 발생 여부 및 처리량을 포함한 배치 요약 정보가 표시됩니다. 그런 다음 각 파일 이름과 해당 파일의 디코딩된 URL을 나열합니다.
<div class="content-img-align-center">
<div class="center-image-wrapper">
<img src="/static-assets/qr/how-to/async-and-multithreading/test2-async-output.webp" alt="병렬 배치 처리 결과가 표시되는 터미널 출력: 처리된 파일 10개, 발견된 QR 코드, 실패 횟수, 처리량 및 파일 이름별 디코딩된 URL" class="img-responsive add-shadow" />
</div>
</div>
[테스트용 QR 코드 입력 이미지 10개가 포함된 배치 파일(batch-qr-images.zip)을 다운로드하세요.](/static-assets/qr/how-to/async-and-multithreading/batch-qr-images.zip)
`ConcurrentBag<t>`는 잠금 없이 모든 스레드의 결과를 수집합니다. 스레드 안전 카운터는 오류를 추적하고, 각 파일에 대해 try-catch를 사용하여 하나의 불량 이미지가 전체 배치 작업을 중단시키지 않도록 합니다. 이 접근 방식은 [오류 처리 방법](/csharp/qr/how-to/detailed-error-messages) 에서 설명하는 오류 격리 패턴을 따릅니다.
현재 CPU 코어 수에 맞추기 위해 `MaxDegreeOfParallelism`를 `Environment.ProcessorCount`로 설정하십시오. 추가 스레드를 사용하면 오버헤드가 증가하고 성능이 향상되지 않으며, 특히 CPU 사용량이 많은 머신러닝 모델의 경우 더욱 그렇습니다.
---
## 비동기 처리와 병렬 처리의 결합
대량 파이프라인의 경우, `SemaphoreSlim`와 `Task.WhenAll`를 짝지어 동시성을 제한하십시오. `Parallel.ForEach`와는 달리 이 패턴은 I/O를 비차단 상태로 유지하여 대량 작업에서 스레드 풀 포화도를 방지하고, 한 번에 얼마나 많은 디코드가 실행되는지를 제어합니다.
### 입력
동시성 파이프라인을 통해 처리된 스무 개의 QR 코드 테스트 이미지 중 네 개. 각 이미지는 URL을 인코딩하고 `SemaphoreSlim`를 통해 제한된 동시성을 사용하여 병렬로 디코딩됩니다.
<div class="competitors-section__wrapper-even-1">
<div class="competitors__card" style="width: 47%;">
<img src="/static-assets/qr/how-to/async-and-multithreading/input-pipeline-qr-1.webp"
alt="파이프라인 QR 코드 입력 이미지 20개 중 1번"
class="img-responsive add-shadow" style="max-width: 200px;" />
<p class="competitors__download-link" style="color: #181818; font-style: italic;">
이미지 1 (파이프라인 1/20)
</p>
</div>
<div class="competitors__card" style="width: 47%;">
<img src="/static-assets/qr/how-to/async-and-multithreading/input-pipeline-qr-2.webp"
alt="파이프라인 QR 코드 입력 이미지 20개 중 2번"
class="img-responsive add-shadow" style="max-width: 200px;" />
<p class="competitors__download-link" style="color: #181818; font-style: italic;">
이미지 2 (20개 파이프라인 중 2번째 파이프라인)
</p>
</div>
</div>
<div class="competitors-section__wrapper-even-1">
<div class="competitors__card" style="width: 47%;">
<img src="/static-assets/qr/how-to/async-and-multithreading/input-pipeline-qr-3.webp"
alt="파이프라인 QR 코드 입력 이미지 20개 중 3번"
class="img-responsive add-shadow" style="max-width: 200px;" />
<p class="competitors__download-link" style="color: #181818; font-style: italic;">
이미지 3 (20개 파이프라인 중 3번째 파이프라인)
</p>
</div>
<div class="competitors__card" style="width: 47%;">
<img src="/static-assets/qr/how-to/async-and-multithreading/input-pipeline-qr-4.webp"
alt="파이프라인 QR 코드 입력 이미지 20개 중 4번"
class="img-responsive add-shadow" style="max-width: 200px;" />
<p class="competitors__download-link" style="color: #181818; font-style: italic;">
이미지 4 (20개 파이프라인 중 4번째 파이프라인)
</p>
</div>
</div>
```cs
:path=/static-assets/qr/content-code-examples/how-to/async-and-multithreading/semaphore-pipeline.cs
```
### 산출
파이프라인이 완료되면 콘솔에 요약 정보가 표시됩니다. 총 디코딩된 QR 코드 수, 소스 파일 수, 소요 시간, 그리고 각 파일 이름과 디코딩된 URL이 차례로 표시됩니다.
<div class="content-img-align-center">
<div class="center-image-wrapper">
<img src="/static-assets/qr/how-to/async-and-multithreading/test3async-output.webp" alt="파이프라인 결과가 표시되는 터미널 출력: 20개 파일에서 추출한 20개의 QR 코드와 파일 이름별로 디코딩된 URL" class="img-responsive add-shadow" />
</div>
</div>
[테스트 파이프라인용 QR 코드 입력 이미지 20개 전체를 다운로드하세요(high-volume-qr-images.zip).](/static-assets/qr/how-to/async-and-multithreading/high-volume-qr-images.zip)
처리량을 높이려면 세마포어 제한을 사용 가능한 코어 수에 맞추거나, 대용량 이미지로 인해 메모리 부족이 우려되는 경우에는 세마포어 제한을 낮추십시오.
---
## 추가 자료
- [ML 스캐닝 예시](https://ironsoftware.com/csharp/qr/examples/read-qr-with-machine-learning/) : 코드 샘플과 스캔 모드 비교.
- [QR 코드 읽는 방법](https://ironsoftware.com/csharp/qr/how-to/read-qr-codes-from-image/) : 입력 구성 및 기본 읽기 패턴.
- [QR 코드 생성기 사용법](https://ironsoftware.com/csharp/qr/tutorials/csharp-qr-code-generator/) : 스타일링을 포함한 생성 방법.
- [QRReader API 참조](https://ironsoftware.com/csharp/qr/object-reference/api/IronQr.QrReader.html) : 메서드 시그니처 및 설명.
- [QrWriter API 참조](https://ironsoftware.com/csharp/qr/object-reference/api/IronQr.QrWriter.html) : 모든 쓰기 오버로드.
- [오류 처리 방법](/csharp/qr/how-to/detailed-error-messages) : 파일별 오류 격리 및 로깅 패턴.
[라이선스 옵션 보기](https://ironsoftware.com/csharp/qr/licensing/) when the pipeline is ready for production.
WPF 버튼 핸들러에서 이로 인해 디코딩이 완료될 때까지 UI가 정지됩니다. 수백 개의 이미지를 처리하는 일괄 작업에서 CPU 코어는 병렬로 작업할 수 있음에도 불구하고 유휴 상태로 남게 됩니다. IronQR의 ReadAsync 메서드는 개별 읽기를 대기 가능한 작업으로 분산하며, 표준 Read 메서드는 배치 처리량에 대해 Parallel.ForEach 및 Task.WhenAll와 함께 작동합니다.
이 가이드는 QR 코드를 비동기적으로 처리하는 방법, 배치 읽기 작업을 CPU 코어에 분산하는 방법, 그리고 대용량 파이프라인을 위해 두 가지 방식을 결합하는 방법을 보여줍니다.
빠른 시작: QR 코드 비동기 처리
이미지를 로드하고 호출 스레드를 차단하지 않고 디코딩된 결과를 기다립니다.
1Install IronQR with NuGet Package Manager
PM > Install-Package IronQR
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);
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);
ReadAsync는 대기 가능한 작업을 반환하여, WPF/MAUI 이벤트 핸들러, ASP.NET 컨트롤러 작업, 또는 어떠한 비동기 메서드와도 호환되도록 합니다. 입력은 이미지 비트맵으로 구성되어야 하며, 파일 경로에 대한 오버로드는 없습니다.
쓰기 작업은 동기식이며 비동기식 변형은 없습니다. 파일 I/O 중 스레드를 차단하지 않으려면, 비트맵에서 내보내진 원시 바이트를 사용하여 File.WriteAllBytesAsync()에 저장 단계를 래핑하십시오.
입력
QR 코드 이벤트 배지를 스캔하고 다시 생성하여 비동기 읽기/쓰기 패턴을 시연합니다.
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.Auto);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();awaitFile.WriteAllBytesAsync("output-qr.png", pngBytes);
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.Auto);
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);
C#
산출
터미널은 디코드된 QR 유형 및 값을 [QrType] Value 형식으로 표시하고 output-qr.png이 저장되었다는 것을 확인합니다.
QrScanMode.Auto는 ML 감지 및 기본 스캔 패스를 모두 실행하여 각 결과에 디코드된 값과 QR 유형을 채웁니다. OnlyDetectionModel는 더 빠르지만 바운딩 박스 좌표만 반환하며, 값 필드는 비어 있습니다. 인코딩된 내용이 필요할 때는 Auto를 사용하십시오.
멀티스레딩을 이용한 QR 코드 처리
독립적으로 디코드할 수 있는 이미지의 경우, Parallel.ForEach는 사용 가능한 CPU 코어 전체에 작업을 분산시킵니다. 각 반복마다 별도의 QrReader 인스턴스가 안전한 기본값입니다. 이는 IronQR이 공유된 판독기 인스턴스에 대해 명시적인 스레드 안정성 보장을 하지 않기 때문입니다.
입력
병렬 배치 스캔에 사용된 10개의 QR 코드 테스트 이미지 중 4개입니다. 각 이미지는 URL을 인코딩하고 런타임에 qr-images/ 폴더에서 읽어옵니다.
이미지 1 (10개 중 1번째 배치)
이미지 2 (10개 중 2번째 배치)
이미지 3 (10개 중 3번째 배치)
이미지 4 (10개 중 4번째 배치)
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<(stringFile, stringValue)>();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.Auto); // 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");
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.Auto);
// 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");
C#
산출
콘솔에는 처리된 파일 수, 처리 시간, 발견된 QR 코드 수, 오류 발생 여부 및 처리량을 포함한 배치 요약 정보가 표시됩니다. 그런 다음 각 파일 이름과 해당 파일의 디코딩된 URL을 나열합니다.
ConcurrentBag<t>는 잠금 없이 모든 스레드의 결과를 수집합니다. 스레드 안전 카운터는 오류를 추적하고, 각 파일에 대해 try-catch를 사용하여 하나의 불량 이미지가 전체 배치 작업을 중단시키지 않도록 합니다. 이 접근 방식은 오류 처리 방법 에서 설명하는 오류 격리 패턴을 따릅니다.
현재 CPU 코어 수에 맞추기 위해 MaxDegreeOfParallelism를 Environment.ProcessorCount로 설정하십시오. 추가 스레드를 사용하면 오버헤드가 증가하고 성능이 향상되지 않으며, 특히 CPU 사용량이 많은 머신러닝 모델의 경우 더욱 그렇습니다.
비동기 처리와 병렬 처리의 결합
대량 파이프라인의 경우, SemaphoreSlim와 Task.WhenAll를 짝지어 동시성을 제한하십시오. Parallel.ForEach와는 달리 이 패턴은 I/O를 비차단 상태로 유지하여 대량 작업에서 스레드 풀 포화도를 방지하고, 한 번에 얼마나 많은 디코드가 실행되는지를 제어합니다.
입력
동시성 파이프라인을 통해 처리된 스무 개의 QR 코드 테스트 이미지 중 네 개. 각 이미지는 URL을 인코딩하고 SemaphoreSlim를 통해 제한된 동시성을 사용하여 병렬로 디코딩됩니다.
이미지 1 (파이프라인 1/20)
이미지 2 (20개 파이프라인 중 2번째 파이프라인)
이미지 3 (20개 파이프라인 중 3번째 파이프라인)
이미지 4 (20개 파이프라인 중 4번째 파이프라인)
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<(stringFile, stringValue)>();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); // Auto: runs ML detection plus a basic scan so result.Value is populated var input = new QrImageInput(bmp, QrScanMode.Auto); 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(); }});awaitTask.WhenAll(tasks);sw.Stop();Console.WriteLine($"Pipeline complete: {results.Count} QR codes from {files.Length} files in {sw.Elapsed.TotalSeconds:F1}s");
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);
// Auto: runs ML detection plus a basic scan so result.Value is populated
var input = new QrImageInput(bmp, QrScanMode.Auto);
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");
C#
산출
파이프라인이 완료되면 콘솔에 요약 정보가 표시됩니다. 총 디코딩된 QR 코드 수, 소스 파일 수, 소요 시간, 그리고 각 파일 이름과 디코딩된 URL이 차례로 표시됩니다.
커티스 차우는 칼턴 대학교에서 컴퓨터 과학 학사 학위를 취득했으며, Node.js, TypeScript, JavaScript, React를 전문으로 하는 프론트엔드 개발자입니다. 직관적이고 미적으로 뛰어난 사용자 인터페이스를 만드는 데 열정을 가진 그는 최신 프레임워크를 활용하고, 잘 구성되고 시각적으로 매력적인 매뉴얼을 제작하는 것을 즐깁니다.