0Pricing
C# Academy · Pelajaran

Tugas Berkala & Timer

Jadwalkan pekerjaan berulang dengan PeriodicTimer dan System.Threading.Timer, lalu kelola pembatalan dengan baik.

Tugas Berkala & Timer adalah pelajaran C# Academy gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar C# Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus C# Academy mencakup 4 pelajaran total.

Menjadwalkan Pekerjaan Berulang

Banyak layanan latar belakang perlu berjalan pada jadwal tetap — setiap 5 menit, setiap jam, atau setiap hari. .NET menyediakan beberapa mekanisme: PeriodicTimer (modern), System.Threading.Timer (klasik), dan Quartz.NET (berbasis cron).

PeriodicTimer: Pilihan Modern

PeriodicTimer (diperkenalkan pada .NET 6) dirancang untuk digunakan dalam metode async. Timer ini terpicu tepat sesuai jadwal tanpa callback dan terintegrasi dengan baik dengan pembatalan.

public class MetricsCollectorService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));

        while (await timer.WaitForNextTickAsync(ct))
        {
            // Runs every 60 seconds
            await CollectMetricsAsync(ct);
        }
        // Loop exits cleanly when ct is cancelled
    }
}

PeriodicTimer vs Perulangan Task.Delay

PeriodicTimer lebih baik daripada perulangan Task.Delay: timer ini terpicu pada interval tetap sejak waktu mulai (bukan waktu mulai + waktu pemrosesan), sehingga mencegah pergeseran jadwal setelah banyak iterasi.

// Task.Delay loop — drifts! (interval = delay + work time)
while (!ct.IsCancellationRequested)
{
    await DoWorkAsync(ct); // takes 2 seconds
    await Task.Delay(5000, ct); // actual period: 7 seconds
}

// PeriodicTimer — no drift
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));
while (await timer.WaitForNextTickAsync(ct))
{
    await DoWorkAsync(ct); // period is still 5 seconds from start
}

System.Threading.Timer

System.Threading.Timer adalah timer klasik kumpulan utas yang menggunakan callback. Timer ini mendukung mode berkala dan sekali jalan. Berhati-hatilah: callback berjalan pada utas kumpulan utas tanpa dukungan async.

public class LegacyTimerService : BackgroundService, IDisposable
{
    private Timer? _timer;

    protected override Task ExecuteAsync(CancellationToken ct)
    {
        // Fire immediately then every 10 seconds
        _timer = new Timer(DoWork, null,
            dueTime:  TimeSpan.Zero,
            period:   TimeSpan.FromSeconds(10));
        return Task.CompletedTask;
    }

    private void DoWork(object? state)
    {
        // Runs on thread pool — avoid async void here
        Console.WriteLine($"Timer fired: {DateTime.UtcNow}");
    }

    public override void Dispose() { _timer?.Dispose(); base.Dispose(); }
}

Eksekusi Tertunda Sekali Jalan

Gunakan PeriodicTimer dengan satu detak, atau gabungkan CancellationTokenSource.CancelAfter dengan Task.Delay, untuk eksekusi tertunda sekali jalan.

// Run once after a delay
public async Task ScheduleOnceAsync(TimeSpan delay, Func<Task> action, CancellationToken ct)
{
    await Task.Delay(delay, ct);
    await action();
}

// Or with CancellationToken:
var cts = new CancellationTokenSource(TimeSpan.FromHours(1));
try
{
    await Task.Delay(Timeout.Infinite, cts.Token);
}
catch (OperationCanceledException)
{
    await RunScheduledJobAsync();
}

PeriodicTimer dengan Penanganan Kesalahan

Selalu bungkus pekerjaan pada setiap detak dalam try/catch agar satu kegagalan tidak menghentikan timer dari pemicuan pada siklus berikutnya.

protected override async Task ExecuteAsync(CancellationToken ct)
{
    using var timer = new PeriodicTimer(TimeSpan.FromMinutes(5));

    while (await timer.WaitForNextTickAsync(ct))
    {
        try
        {
            await SyncInventoryAsync(ct);
        }
        catch (Exception ex)
        {
            // Log but don't crash — next tick will retry
            _logger.LogError(ex, "Inventory sync failed");
        }
    }
}

Menyelaraskan dengan Batas Waktu Jam

Untuk menjalankan pekerjaan pada waktu tertentu (misalnya, setiap jam tepat pada pergantian jam), hitung penundaan awal hingga batas waktu berikutnya sebelum memulai timer.

protected override async Task ExecuteAsync(CancellationToken ct)
{
    // Wait until the next full hour
    var now = DateTime.UtcNow;
    var nextHour = now.Date.AddHours(now.Hour + 1);
    var initialDelay = nextHour - now;

    await Task.Delay(initialDelay, ct);

    // Then tick every hour exactly
    using var timer = new PeriodicTimer(TimeSpan.FromHours(1));

    await RunJobAsync(ct); // run at the first tick
    while (await timer.WaitForNextTickAsync(ct))
        await RunJobAsync(ct);
}

Perlindungan terhadap Detak Bersamaan

Jika pekerjaan dapat berlangsung lebih lama daripada satu periode timer, cegah eksekusi bersamaan dengan SemaphoreSlim atau lewati detak jika pekerjaan sebelumnya masih berjalan.

private readonly SemaphoreSlim _lock = new(1, 1);

protected override async Task ExecuteAsync(CancellationToken ct)
{
    using var timer = new PeriodicTimer(TimeSpan.FromSeconds(30));

    while (await timer.WaitForNextTickAsync(ct))
    {
        // Skip if previous tick is still running
        if (!await _lock.WaitAsync(0, ct))
        {
            _logger.LogWarning("Skipping tick — previous still running");
            continue;
        }
        try { await DoWorkAsync(ct); }
        finally { _lock.Release(); }
    }
}

Jitter untuk Layanan Terdistribusi

Ketika beberapa instans layanan dimulai secara bersamaan, penambahan jitter acak pada penundaan awal mencegah masalah serbuan bersamaan pada sumber daya bersama.

protected override async Task ExecuteAsync(CancellationToken ct)
{
    // Add jitter: 0-5 seconds random delay on startup
    var jitter = TimeSpan.FromSeconds(Random.Shared.NextDouble() * 5);
    await Task.Delay(jitter, ct);

    using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
    while (await timer.WaitForNextTickAsync(ct))
        await RunJobAsync(ct);
}

Dunia Nyata: Layanan Penyegaran Cache

Layanan berkala yang menyegarkan cache terdistribusi setiap 10 menit, dengan jitter dan pemulihan dari kesalahan.

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

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        // Spread startups across instances
        await Task.Delay(TimeSpan.FromSeconds(Random.Shared.Next(0, 30)), ct);

        using var timer = new PeriodicTimer(TimeSpan.FromMinutes(10));
        while (await timer.WaitForNextTickAsync(ct))
        {
            try
            {
                using var scope = _factory.CreateScope();
                var cache = scope.ServiceProvider.GetRequiredService<ICacheService>();
                await cache.RefreshAllAsync(ct);
                _log.LogInformation("Cache refreshed at {Time}", DateTime.UtcNow);
            }
            catch (Exception ex) { _log.LogError(ex, "Cache refresh failed"); }
        }
    }
}

Pemeriksaan Singkat

Mengapa PeriodicTimer lebih disarankan daripada perulangan Task.Delay untuk tugas berulang?

Ringkasan: Tugas Berkala & Timer

Inti pembelajaran:

  • PeriodicTimer: async, menyadari pembatalan, tanpa pergeseran jadwal — pilihan modern
  • System.Threading.Timer: berbasis callback, cocok untuk integrasi dengan kode lama
  • Bungkus pekerjaan pada setiap detak dalam try/catch agar kesalahan tidak menghentikan timer
  • Tambahkan jitter untuk instans terdistribusi guna menghindari serbuan bersamaan
  • Gunakan SemaphoreSlim untuk melewati detak jika detak sebelumnya masih berjalan
  • Hitung penundaan awal agar detak selaras dengan batas waktu jam

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Tugas Berkala & Timer” gratis?

Ya — teks lengkap “Tugas Berkala & Timer” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus C# Academy, upgrade ke CoddyKit PRO. Kursus C# Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Tugas Berkala & Timer”?

Jadwalkan pekerjaan berulang dengan PeriodicTimer dan System.Threading.Timer, lalu kelola pembatalan dengan baik. Kamu berlatih C# Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai C# Academy?

Tidak diperlukan pengalaman sebelumnya. C# Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Tugas Berkala & Timer” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran C# Academy ini?

Ya. Setiap pelajaran C# Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. IHostedService & BackgroundService
  2. Proyek Worker Service
  3. Tugas Berkala & Timer
  4. Pekerjaan Terjadwal Quartz.NET
← Kembali ke C# Academy