C# Academy · บทเรียน

IHostedService และ BackgroundService

ใช้งาน IHostedService และ BackgroundService แบบนามธรรมเพื่อทำงานเบื้องหลังภายในโฮสต์ .NET

บทเรียน 1 จาก 412 ขั้นตอน

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

บริการเบื้องหลังคืออะไร

บริการเบื้องหลังทำงานที่มีอายุยาวนานควบคู่ไปกับแอป ASP.NET Core เช่น ประมวลผลคิวงาน ทำงานตามกำหนดเวลา หรือตรวจสอบทรัพยากร .NET มีอินเทอร์เฟซสองแบบ ได้แก่ IHostedService (แบบพื้นฐาน) และคลาสนามธรรม BackgroundService (เหมาะสำหรับการทำงานเป็นลูป)

อินเทอร์เฟซ IHostedService

IHostedService มีสองเมธอด ได้แก่ StartAsync (เรียกเมื่อโฮสต์เริ่มทำงาน) และ StopAsync (เรียกระหว่างการปิดระบบอย่างราบรื่น) เหมาะสำหรับงานเริ่มต้นหรือปิดระบบที่ทำเพียงครั้งเดียว

public class DatabaseMigratorService : IHostedService
{
    private readonly IServiceScopeFactory _scopeFactory;

    public DatabaseMigratorService(IServiceScopeFactory sf) => _scopeFactory = sf;

    public async Task StartAsync(CancellationToken ct)
    {
        using var scope = _scopeFactory.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        await db.Database.MigrateAsync(ct);
    }

    public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
}

builder.Services.AddHostedService<DatabaseMigratorService>();

BackgroundService: ลูปที่ทำงานต่อเนื่อง

BackgroundService เป็นคลาสนามธรรมที่ใช้งาน IHostedService และมี ExecuteAsync(CancellationToken) ซึ่งเป็นเมธอดที่ทำงานตลอดวงจรอายุของแอป

public class HeartbeatService : BackgroundService
{
    private readonly ILogger<HeartbeatService> _logger;

    public HeartbeatService(ILogger<HeartbeatService> l) => _logger = l;

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        _logger.LogInformation("Heartbeat service started");

        while (!ct.IsCancellationRequested)
        {
            _logger.LogInformation("Heartbeat: {Time}", DateTime.UtcNow);
            await Task.Delay(TimeSpan.FromSeconds(30), ct);
        }

        _logger.LogInformation("Heartbeat service stopping");
    }
}

builder.Services.AddHostedService<HeartbeatService>();

การใช้บริการที่มีขอบเขต

บริการเบื้องหลังทำงานเป็นซิงเกิลตัน หากต้องการใช้บริการที่มีขอบเขต (เช่น DbContext) ให้สร้าง scope ใหม่สำหรับหน่วยงานแต่ละหน่วยโดยใช้ IServiceScopeFactory เสมอ

public class DataSyncService : BackgroundService
{
    private readonly IServiceScopeFactory _factory;
    private readonly ILogger<DataSyncService> _log;

    public DataSyncService(IServiceScopeFactory f, ILogger<DataSyncService> l)
    { _factory = f; _log = l; }

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            using (var scope = _factory.CreateScope())
            {
                var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
                await SyncDataAsync(db, ct);
            } // scope and DbContext disposed here

            await Task.Delay(TimeSpan.FromMinutes(5), ct);
        }
    }
}

การปิดระบบอย่างราบรื่น

เมื่อโฮสต์ปิดระบบ โฮสต์จะส่งโทเคนการยกเลิกออกมา ExecuteAsync ของคุณควรตรวจสอบโทเคนดังกล่าวและทำความสะอาดทรัพยากร โฮสต์จะรอสูงสุด 5 วินาที (ค่าเริ่มต้น) ก่อนบังคับหยุดการทำงาน

protected override async Task ExecuteAsync(CancellationToken ct)
{
    try
    {
        while (!ct.IsCancellationRequested)
        {
            await DoWorkAsync(ct);
            await Task.Delay(1000, ct);
        }
    }
    catch (OperationCanceledException)
    {
        // Normal shutdown — not an error
        _logger.LogInformation("Service stopping due to cancellation");
    }
    finally
    {
        // Cleanup
        await CleanupAsync();
    }
}

คิวงานเบื้องหลังแบบช่องสัญญาณ

รูปแบบที่ใช้กันทั่วไปคือ ให้บริการเบื้องหลังอ่านรายการงานจากคิวช่องสัญญาณ ขณะที่จุดปลายทาง HTTP เพิ่มรายการงานลงในคิว วิธีนี้แยกการจัดการคำขอออกจากการประมวลผลรายการงาน

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

    public ValueTask QueueAsync(Func<CancellationToken, Task> job)
        => _queue.Writer.WriteAsync(job);

    public IAsyncEnumerable<Func<CancellationToken, Task>> DequeueAllAsync(
        CancellationToken ct)
        => _queue.Reader.ReadAllAsync(ct);
}

// Register and use:
builder.Services.AddSingleton<BackgroundTaskQueue>();
builder.Services.AddHostedService<QueueProcessorService>();

การจัดการข้อผิดพลาดในบริการเบื้องหลัง

ข้อยกเว้นที่ไม่ได้จัดการใน ExecuteAsync อาจทำให้บริการหยุดทำงานโดยไม่มีข้อความแจ้งเตือน ควรห่อลูปหลักด้วย try/catch และบันทึกข้อผิดพลาด เพื่อให้บริการยังทำงานต่อได้แม้เกิดความล้มเหลวเป็นรายครั้ง

protected override async Task ExecuteAsync(CancellationToken ct)
{
    while (!ct.IsCancellationRequested)
    {
        try
        {
            await ProcessNextBatchAsync(ct);
        }
        catch (OperationCanceledException) when (ct.IsCancellationRequested)
        {
            break; // normal shutdown
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Background processing error. Retrying in 5s.");
            await Task.Delay(5000, ct); // backoff before retry
        }
    }
}

StopAsync และเวลาหมดการรอระหว่างปิดระบบ

เขียนทับ StopAsync เพื่อดำเนินการทำความสะอาด โฮสต์จะส่งโทเคนการปิดระบบให้ หากการทำความสะอาดใช้เวลานานกว่าระยะเวลาที่กำหนด กระบวนการจะถูกยุติไม่ว่าในกรณีใด

public override async Task StopAsync(CancellationToken stoppingToken)
{
    _logger.LogInformation("Service is stopping...");

    // Signal internal work to stop
    _internalCts.Cancel();

    // Wait for the Execute loop to complete (up to timeout)
    await base.StopAsync(stoppingToken);

    _logger.LogInformation("Service stopped");
}

// Increase the shutdown timeout if needed:
builder.Services.Configure<HostOptions>(opt =>
    opt.ShutdownTimeout = TimeSpan.FromSeconds(30));

บริการเบื้องหลังหลายรายการ

คุณสามารถลงทะเบียนบริการที่โฮสต์ไว้หลายรายการได้ โดยบริการทั้งหมดจะทำงานพร้อมกัน โฮสต์จะเริ่มบริการตามลำดับและหยุดบริการย้อนกลับตามลำดับ

builder.Services.AddHostedService<DatabaseMigratorService>(); // startup task
builder.Services.AddHostedService<MetricsCollectorService>();  // continuous
builder.Services.AddHostedService<EmailNotificationService>(); // continuous
builder.Services.AddHostedService<CacheWarmupService>();       // startup task

// All run concurrently after app start
// Stopped in reverse registration order on shutdown

ตัวอย่างการใช้งานจริง: ผู้ปฏิบัติงานประมวลผลคำสั่งซื้อ

บริการเบื้องหลังที่ประมวลผลคำสั่งซื้อที่รอดำเนินการจากคิวในฐานข้อมูล โดยกำหนด scope และจัดการข้อผิดพลาดอย่างเหมาะสม

public class OrderProcessorService : BackgroundService
{
    private readonly IServiceScopeFactory _factory;
    private readonly ILogger<OrderProcessorService> _log;

    public OrderProcessorService(IServiceScopeFactory f, ILogger<OrderProcessorService> l)
    { _factory = f; _log = l; }

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            try
            {
                using var scope = _factory.CreateScope();
                var processor = scope.ServiceProvider.GetRequiredService<IOrderProcessor>();
                int count = await processor.ProcessPendingAsync(ct);
                _log.LogInformation("Processed {Count} orders", count);
            }
            catch (Exception ex) when (!ct.IsCancellationRequested)
            {
                _log.LogError(ex, "Order processing failed");
            }
            await Task.Delay(TimeSpan.FromSeconds(10), ct);
        }
    }
}

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

เหตุใดบริการเบื้องหลังจึงต้องใช้ IServiceScopeFactory แทนการฉีด DbContext โดยตรง

สรุป: IHostedService และ BackgroundService

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

  • IHostedService: StartAsync/StopAsync — สำหรับงานเริ่มต้นหรือปิดระบบ
  • BackgroundService: ลูป ExecuteAsync — สำหรับงานเบื้องหลังที่ทำต่อเนื่อง
  • ใช้ IServiceScopeFactory สำหรับการพึ่งพาที่มีขอบเขตภายในบริการเบื้องหลังเสมอ
  • ตรวจสอบโทเคนการยกเลิกในทุก await เพื่อการปิดระบบอย่างราบรื่น
  • ห่อส่วนเนื้อหาของลูปด้วย try/catch เพื่อป้องกันไม่ให้บริการหยุดทำงานเมื่อเกิดข้อผิดพลาด
  • ใช้ HostOptions.ShutdownTimeout หากการทำความสะอาดต้องใช้เวลานานกว่า 5 วินาที
เริ่มต้นได้ฟรี

เรียนรู้ C# ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
93
บทเรียน
346

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

บทเรียน “IHostedService และ BackgroundService” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “IHostedService และ BackgroundService”

ใช้งาน IHostedService และ BackgroundService แบบนามธรรมเพื่อทำงานเบื้องหลังภายในโฮสต์ .NET คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

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

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

บทเรียน “IHostedService และ BackgroundService” ใช้เวลานานแค่ไหน

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

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

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

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

  1. IHostedService และ BackgroundService
  2. โครงการ Worker Service
  3. งานตามช่วงเวลาและตัวจับเวลา
  4. งานตามกำหนดเวลาด้วย Quartz.NET
← กลับไปที่ C# Academy