0Pricing
C# Academy · บทเรียน

System.Threading.Channels

สร้างสายงานแบบผู้ผลิต-ผู้บริโภคด้วย Channel, ChannelWriter และ ChannelReader เพื่อควบคุมแรงดันย้อนกลับ

System.Threading.Channels เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

แชนเนลคืออะไร

System.Threading.Channels (เริ่มใช้ใน .NET Core 3) มีคิวผู้ผลิต-ผู้บริโภคที่มีประสิทธิภาพสูงและปลอดภัยต่อเธรด ต่างจาก BlockingCollection แชนเนลทำงานแบบอะซิงโครนัสเต็มรูปแบบและจัดสรรหน่วยความจำอย่างมีประสิทธิภาพ จึงเหมาะอย่างยิ่งสำหรับไปป์ไลน์ภายในกระบวนการ

การสร้างแชนเนล

แชนเนลสร้างขึ้นด้วยตัวสร้าง เลือกแบบไม่จำกัดขนาด (ไม่มีขีดจำกัด) หรือแบบจำกัดขนาด (มีความจุจำกัดพร้อมแรงกดดันย้อนกลับ) ตัวสร้างจะส่งคืน Channel<T> ที่มี Writer และ Reader

using System.Threading.Channels;

// Unbounded: unlimited capacity, no backpressure
var unbounded = Channel.CreateUnbounded<string>();

// Bounded: max 100 items; writer waits when full
var bounded = Channel.CreateBounded<string>(100);

// Bounded with drop-oldest strategy:
var dropping = Channel.CreateBounded<string>(new BoundedChannelOptions(50)
{
    FullMode = BoundedChannelFullMode.DropOldest
});

การเขียนลงแชนเนล

ผู้ผลิตเขียนรายการโดยใช้ WriteAsync (รอหากแชนเนลแบบจำกัดขนาดเต็ม) หรือ TryWrite (ส่งคืนค่า false ทันทีหากเต็ม) ส่งสัญญาณว่าดำเนินการเสร็จสิ้นด้วย Writer.Complete()

var channel = Channel.CreateUnbounded<int>();

// Producer task
var producer = Task.Run(async () =>
{
    for (int i = 0; i < 100; i++)
    {
        await channel.Writer.WriteAsync(i);
        await Task.Delay(10);
    }
    channel.Writer.Complete(); // signal no more items
});

การอ่านจากแชนเนล

ผู้บริโภคอ่านข้อมูลด้วย ReadAllAsync() (แนวทางที่ง่ายที่สุด) หรือใช้ ReadAsync/TryRead เพื่อควบคุมได้มากขึ้น ReadAllAsync จะเสร็จสิ้นเมื่อผู้เขียนเรียก Complete()

// Consumer task
var consumer = Task.Run(async () =>
{
    await foreach (var item in channel.Reader.ReadAllAsync())
    {
        Console.WriteLine($"Processed: {item}");
        // Naturally paced — waits for next item
    }
    Console.WriteLine("Channel completed");
});

await Task.WhenAll(producer, consumer);

ผู้ผลิตหลายราย

แชนเนลปลอดภัยต่อเธรด ผู้ผลิตหลายรายสามารถเขียนพร้อมกันได้โดยไม่ต้องล็อก เรียก Writer.Complete() หลังจากผู้ผลิตทั้งหมดทำงานเสร็จแล้วเท่านั้น

var channel = Channel.CreateUnbounded<WorkItem>();

var producers = Enumerable.Range(0, 4).Select(id =>
    Task.Run(async () =>
    {
        for (int i = 0; i < 25; i++)
            await channel.Writer.WriteAsync(new WorkItem(id, i));
    }));

// Wait for all producers before completing the writer
await Task.WhenAll(producers);
channel.Writer.Complete();

ผู้บริโภคหลายราย (การกระจายงาน)

เรียกใช้งานหลาย task ของผู้บริโภคกับตัวอ่านแชนเนลเดียวกัน เพื่อประมวลผลแบบขนาน แต่ละรายการจะถูกส่งให้ผู้บริโภคเพียงรายเดียวเท่านั้น (เป็นการแบ่งส่วน ไม่ใช่การกระจายไปให้ทุกคน)

var channel = Channel.CreateBounded<WorkItem>(100);

// 4 parallel consumers
var consumers = Enumerable.Range(0, 4).Select(id =>
    Task.Run(async () =>
    {
        await foreach (var item in channel.Reader.ReadAllAsync())
        {
            await ProcessItemAsync(item);
            Console.WriteLine($"Consumer {id} processed {item.Id}");
        }
    }));

await Task.WhenAll(consumers);

รูปแบบไปป์ไลน์

เชื่อมแชนเนลเข้าด้วยกันเป็นไปป์ไลน์การประมวลผล: แต่ละขั้นตอนอ่านข้อมูลจากแชนเนลหนึ่ง ประมวลผลรายการ แล้วเขียนลงแชนเนลถัดไป ขั้นตอนต่าง ๆ จะทำงานพร้อมกันโดยมีแรงกดดันย้อนกลับตามธรรมชาติ

// Stage 1: raw data
var stage1 = Channel.CreateBounded<string>(50);
// Stage 2: parsed
var stage2 = Channel.CreateBounded<ParsedRecord>(50);
// Stage 3: enriched
var stage3 = Channel.CreateBounded<EnrichedRecord>(50);

var parse   = ParseStageAsync(stage1.Reader, stage2.Writer);
var enrich  = EnrichStageAsync(stage2.Reader, stage3.Writer);
var persist = PersistStageAsync(stage3.Reader);

await Task.WhenAll(parse, enrich, persist);

แรงกดดันย้อนกลับด้วยแชนเนลแบบจำกัดขนาด

แชนเนลแบบจำกัดขนาดจะใช้แรงกดดันย้อนกลับโดยอัตโนมัติ เมื่อแชนเนลเต็ม WriteAsync จะระงับผู้ผลิตไว้จนกว่าผู้บริโภคจะนำรายการออก ไม่จำเป็นต้องเขียนโค้ดควบคุมอัตราด้วยตนเอง

// Bounded channel: max 10 items
var channel = Channel.CreateBounded<string>(10);

// Fast producer
var producer = Task.Run(async () =>
{
    for (int i = 0; i < 1000; i++)
    {
        // WriteAsync waits when channel has 10 items
        await channel.Writer.WriteAsync($"item-{i}");
        // Producer is naturally slowed to consumer speed
    }
    channel.Writer.Complete();
});

// Slow consumer
var consumer = Task.Run(async () =>
{
    await foreach (var item in channel.Reader.ReadAllAsync())
    {
        await Task.Delay(50); // simulate slow processing
        Console.WriteLine(item);
    }
});

การจัดการข้อผิดพลาดในแชนเนล

ส่งข้อยกเว้นไปยัง Writer.Complete(exception) เพื่อส่งต่อข้อผิดพลาดไปยังผู้อ่านทั้งหมดที่กำลังรอ ผู้อ่านจะเห็นข้อยกเว้นเมื่ออ่านแชนเนลครั้งถัดไป

var channel = Channel.CreateUnbounded<int>();

var producer = Task.Run(async () =>
{
    try
    {
        for (int i = 0; i < 100; i++)
        {
            if (i == 50) throw new Exception("Producer failed at 50");
            await channel.Writer.WriteAsync(i);
        }
        channel.Writer.Complete();
    }
    catch (Exception ex)
    {
        channel.Writer.Complete(ex); // propagate to reader
    }
});

try
{
    await foreach (var item in channel.Reader.ReadAllAsync())
        Console.WriteLine(item);
}
catch (Exception ex)
{
    Console.Error.WriteLine($"Channel error: {ex.Message}");
}

การใช้งานจริง: คิวงานเบื้องหลัง

คิวงานเบื้องหลังที่ใช้บริการโฮสต์และแชนเนล: คำขอ HTTP จะนำงานเข้าคิว แล้วผู้ปฏิบัติงานเบื้องหลังจะประมวลผลทีละงาน

public class JobQueue
{
    private readonly Channel<Func<CancellationToken, Task>> _queue
        = Channel.CreateBounded<Func<CancellationToken, Task>>(100);

    public ChannelWriter<Func<CancellationToken, Task>> Writer => _queue.Writer;
    public ChannelReader<Func<CancellationToken, Task>> Reader => _queue.Reader;
}

public class JobProcessor : BackgroundService
{
    private readonly JobQueue _queue;
    public JobProcessor(JobQueue q) => _queue = q;

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        await foreach (var job in _queue.Reader.ReadAllAsync(ct))
            await job(ct);
    }
}

ตรวจสอบความเข้าใจ

จะเกิดอะไรขึ้นเมื่อผู้ผลิตเรียก WriteAsync บนแชนเนลแบบจำกัดขนาดที่เต็มแล้ว

สรุป: System.Threading.Channels

ประเด็นสำคัญ:

  • แชนเนลมีคิวผู้ผลิต-ผู้บริโภคที่มีประสิทธิภาพสูงและรองรับอะซิงโครนัสโดยธรรมชาติ
  • แบบไม่จำกัดขนาด: ไม่มีขีดจำกัด แบบจำกัดขนาด: มีความจุจำกัดพร้อมแรงกดดันย้อนกลับหรือกลยุทธ์การทิ้งข้อมูล
  • ผู้ผลิตและผู้บริโภคหลายรายทำงานได้อย่างปลอดภัยโดยไม่ต้องล็อก
  • ReadAllAsync() + await foreach = รูปแบบการบริโภคที่สะอาดที่สุด
  • เชื่อมแชนเนลเป็นไปป์ไลน์เพื่อประมวลผลพร้อมกันแบบแบ่งตามขั้นตอน
  • เรียก Writer.Complete() (หรือ Complete(exception)) เพื่อส่งสัญญาณว่าสตรีมสิ้นสุด

คำถามที่พบบ่อย

บทเรียน “System.Threading.Channels” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “System.Threading.Channels” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “System.Threading.Channels”

สร้างสายงานแบบผู้ผลิต-ผู้บริโภคด้วย Channel, ChannelWriter และ ChannelReader เพื่อควบคุมแรงดันย้อนกลับ คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “System.Threading.Channels” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม

ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. IAsyncEnumerable และ await foreach
  2. System.Threading.Channels
  3. ValueTask และการหลีกเลี่ยงการจัดสรรหน่วยความจำ
  4. ConfigureAwait และบริบทการซิงโครไนซ์
← กลับไปที่ C# Academy