Timer Terbaik untuk digunakan dalam layanan Windows

108

Saya perlu membuat beberapa layanan windows yang akan dijalankan setiap periode waktu N.
Pertanyaannya adalah:
Kontrol pengatur waktu mana yang harus saya gunakan: System.Timers.Timeratau System.Threading.Timersatu? Apakah itu mempengaruhi sesuatu?

Saya bertanya karena saya mendengar banyak bukti untuk pekerjaan yang tidak benar System.Timers.Timerdi layanan windows.
Terima kasih.

shA.t
sumber

Jawaban:

118

Keduanya System.Timers.Timerdan System.Threading.Timerakan bekerja untuk layanan.

Pengatur waktu yang ingin Anda hindari adalah System.Web.UI.Timerdan System.Windows.Forms.Timer, yang masing-masing untuk aplikasi ASP dan WinForms. Menggunakan itu akan menyebabkan layanan memuat rakitan tambahan yang tidak benar-benar diperlukan untuk jenis aplikasi yang Anda buat.

Gunakan System.Timers.Timerseperti contoh berikut (juga, pastikan Anda menggunakan variabel tingkat kelas untuk mencegah pengumpulan sampah, seperti yang dinyatakan dalam jawaban Tim Robinson):

using System;
using System.Timers;

public class Timer1
{
    private static System.Timers.Timer aTimer;

    public static void Main()
    {
        // Normally, the timer is declared at the class level,
        // so that it stays in scope as long as it is needed.
        // If the timer is declared in a long-running method,  
        // KeepAlive must be used to prevent the JIT compiler 
        // from allowing aggressive garbage collection to occur 
        // before the method ends. (See end of method.)
        //System.Timers.Timer aTimer;

        // Create a timer with a ten second interval.
        aTimer = new System.Timers.Timer(10000);

        // Hook up the Elapsed event for the timer.
        aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);

        // Set the Interval to 2 seconds (2000 milliseconds).
        aTimer.Interval = 2000;
        aTimer.Enabled = true;

        Console.WriteLine("Press the Enter key to exit the program.");
        Console.ReadLine();

        // If the timer is declared in a long-running method, use
        // KeepAlive to prevent garbage collection from occurring
        // before the method ends.
        //GC.KeepAlive(aTimer);
    }

    // Specify what you want to happen when the Elapsed event is 
    // raised.
    private static void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
    }
}

/* This code example produces output similar to the following:

Press the Enter key to exit the program.
The Elapsed event was raised at 5/20/2007 8:42:27 PM
The Elapsed event was raised at 5/20/2007 8:42:29 PM
The Elapsed event was raised at 5/20/2007 8:42:31 PM
...
 */

Jika Anda memilih System.Threading.Timer, Anda dapat menggunakan sebagai berikut:

using System;
using System.Threading;

class TimerExample
{
    static void Main()
    {
        AutoResetEvent autoEvent     = new AutoResetEvent(false);
        StatusChecker  statusChecker = new StatusChecker(10);

        // Create the delegate that invokes methods for the timer.
        TimerCallback timerDelegate = 
            new TimerCallback(statusChecker.CheckStatus);

        // Create a timer that signals the delegate to invoke 
        // CheckStatus after one second, and every 1/4 second 
        // thereafter.
        Console.WriteLine("{0} Creating timer.\n", 
            DateTime.Now.ToString("h:mm:ss.fff"));
        Timer stateTimer = 
                new Timer(timerDelegate, autoEvent, 1000, 250);

        // When autoEvent signals, change the period to every 
        // 1/2 second.
        autoEvent.WaitOne(5000, false);
        stateTimer.Change(0, 500);
        Console.WriteLine("\nChanging period.\n");

        // When autoEvent signals the second time, dispose of 
        // the timer.
        autoEvent.WaitOne(5000, false);
        stateTimer.Dispose();
        Console.WriteLine("\nDestroying timer.");
    }
}

class StatusChecker
{
    int invokeCount, maxCount;

    public StatusChecker(int count)
    {
        invokeCount  = 0;
        maxCount = count;
    }

    // This method is called by the timer delegate.
    public void CheckStatus(Object stateInfo)
    {
        AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
        Console.WriteLine("{0} Checking status {1,2}.", 
            DateTime.Now.ToString("h:mm:ss.fff"), 
            (++invokeCount).ToString());

        if(invokeCount == maxCount)
        {
            // Reset the counter and signal Main.
            invokeCount  = 0;
            autoEvent.Set();
        }
    }
}

Kedua contoh tersebut berasal dari halaman MSDN.

Andrew Moore
sumber
1
Mengapa Anda menyarankan: GC.KeepAlive (aTimer) ;, aTimer adalah variabel instance kan, jadi misalnya jika itu adalah variabel instance form, akan selalu ada referensinya selama ada form kan?
giorgim
37

Jangan gunakan layanan untuk ini. Buat aplikasi normal dan buat tugas terjadwal untuk menjalankannya.

Ini adalah praktik terbaik yang umum dipegang. Jon Galloway setuju dengan saya. Atau mungkin sebaliknya. Either way, faktanya adalah bahwa itu bukan praktik terbaik untuk membuat layanan windows untuk melakukan tugas yang terputus-putus menjalankan pengatur waktu.

"Jika Anda menulis Layanan Windows yang menjalankan pengatur waktu, Anda harus mengevaluasi ulang solusi Anda."

–Jon Galloway, manajer program komunitas ASP.NET MVC, penulis, pahlawan super paruh waktu

Komunitas
sumber
26
Jika layanan Anda dimaksudkan untuk berjalan sepanjang hari, mungkin layanan lebih masuk akal daripada tugas terjadwal. Atau, memiliki layanan dapat menyederhanakan admin dan logging untuk grup infrastruktur yang tidak secerdas tim aplikasi. Namun, mempertanyakan asumsi bahwa layanan diperlukan sepenuhnya valid, dan peringkat negatif ini tidak layak. +1 untuk keduanya.
sfuqua
2
@rhmn. Tugas terjadwal adalah bagian inti dari OS. Harap simpan FUD Anda untuk diri sendiri.
4
@MR: Saya bukan seorang penginjil, saya seorang realis. Dan kenyataan telah mengajari saya bahwa tugas yang dijadwalkan tidak "sangat bermasalah". Faktanya, terserah Anda sebagai orang yang membuat klaim untuk mendukungnya. Jika tidak, yang Anda lakukan hanyalah menyebarkan ketakutan, ketidakpastian, dan keraguan.
13
Saya tidak suka masuk ke dalam lumpur di sini, tetapi saya harus sedikit membela MR. Saya memiliki beberapa aplikasi penting yang berjalan di perusahaan saya yang merupakan aplikasi konsol windows. Saya telah menggunakan Windows Task Scheduler untuk menjalankan semuanya. Setidaknya pada 5 kesempatan sekarang, kami mengalami masalah di mana Layanan Penjadwal "bingung" entah bagaimana. Tugas tidak dijalankan dan beberapa dalam keadaan aneh. Satu-satunya solusi adalah me-reboot server atau menghentikan dan memulai layanan Scheduler. Bukan sesuatu yang dapat saya lakukan tanpa hak admin dan tidak dapat diterima dalam lingkungan produksi. Hanya $ 0,02 saya.
SpaceCowboy74
6
Ini sebenarnya terjadi pada saya di beberapa server (sejauh ini 3). Tidak akan mengatakan itu norma. Hanya mengatakan terkadang tidak buruk untuk menerapkan metode Anda sendiri dalam melakukan sesuatu.
SpaceCowboy74
7

Salah satu harus bekerja dengan baik. Faktanya, System.Threading.Timer menggunakan System.Timers.Timer secara internal.

Karena itu, mudah untuk menyalahgunakan System.Timers.Timer. Jika Anda tidak menyimpan objek Timer dalam variabel di suatu tempat, maka objek tersebut kemungkinan besar akan dibuang. Jika itu terjadi, pengatur waktu Anda tidak akan menyala lagi. Panggil metode Dispose untuk menghentikan timer, atau gunakan kelas System.Threading.Timer, yang merupakan pembungkus yang sedikit lebih bagus.

Masalah apa yang sudah Anda lihat sejauh ini?

Tim Robinson
sumber
Membuat saya bertanya-tanya mengapa aplikasi Windows Phone hanya dapat mengakses System.Threading.Timer.
Stonetip
Windows phone mungkin memiliki versi kerangka kerja yang lebih ringan, memiliki semua kode tambahan untuk dapat menggunakan kedua metode tersebut mungkin tidak diperlukan sehingga tidak disertakan. Saya pikir jawaban Nick, memberikan alasan yang lebih baik mengapa ponsel Windows tidak memiliki akses System.Timers.Timerkarena tidak akan menangani Pengecualian yang diberikan padanya.
Maleakhi
2

Saya setuju dengan komentar sebelumnya yang mungkin paling baik untuk mempertimbangkan pendekatan yang berbeda. Saran saya adalah menulis aplikasi konsol dan menggunakan penjadwal windows:

Ini akan:

  • Kurangi kode pipa yang mereplikasi perilaku penjadwal
  • Berikan fleksibilitas yang lebih besar dalam hal perilaku penjadwalan (misalnya, hanya dijalankan pada akhir pekan) dengan semua logika penjadwalan disarikan dari kode aplikasi
  • Memanfaatkan argumen baris perintah untuk parameter tanpa harus menyiapkan nilai konfigurasi di file konfigurasi, dll
  • Jauh lebih mudah untuk men-debug / menguji selama pengembangan
  • Izinkan pengguna dukungan untuk mengeksekusi dengan menjalankan aplikasi konsol secara langsung (mis. Berguna selama situasi dukungan)
pengguna32378
sumber
3
Tapi ini membutuhkan pengguna yang masuk? Jadi layanan mungkin lebih baik jika dijalankan 24/7 di server.
JP Hellemons
1

Seperti yang telah dinyatakan baik System.Threading.Timerdan System.Timers.Timerakan berhasil. Perbedaan besar antara keduanya adalah System.Threading.Timerpembungkus yang mengelilingi yang lainnya.

System.Threading.Timerakan memiliki lebih banyak penanganan pengecualian sementara System.Timers.Timerakan menelan semua pengecualian.

Ini memberi saya masalah besar di masa lalu jadi saya akan selalu menggunakan 'System.Threading.Timer' dan masih menangani pengecualian Anda dengan sangat baik.

Nick N.
sumber
0

Saya tahu utas ini agak tua tetapi berguna untuk skenario tertentu yang saya miliki dan saya pikir perlu dicatat bahwa ada alasan lain mengapa System.Threading.Timermungkin pendekatan yang baik. Saat Anda harus menjalankan Pekerjaan secara berkala yang mungkin membutuhkan waktu lama dan Anda ingin memastikan bahwa seluruh masa tunggu digunakan di antara pekerjaan atau jika Anda tidak ingin pekerjaan dijalankan lagi sebelum pekerjaan sebelumnya selesai dalam kasus di mana pekerjaan membutuhkan waktu lebih lama dari periode timer. Anda bisa menggunakan yang berikut ini:

using System;
using System.ServiceProcess;
using System.Threading;

    public partial class TimerExampleService : ServiceBase
    {
        private AutoResetEvent AutoEventInstance { get; set; }
        private StatusChecker StatusCheckerInstance { get; set; }
        private Timer StateTimer { get; set; }
        public int TimerInterval { get; set; }

        public CaseIndexingService()
        {
            InitializeComponent();
            TimerInterval = 300000;
        }

        protected override void OnStart(string[] args)
        {
            AutoEventInstance = new AutoResetEvent(false);
            StatusCheckerInstance = new StatusChecker();

            // Create the delegate that invokes methods for the timer.
            TimerCallback timerDelegate =
                new TimerCallback(StatusCheckerInstance.CheckStatus);

            // Create a timer that signals the delegate to invoke 
            // 1.CheckStatus immediately, 
            // 2.Wait until the job is finished,
            // 3.then wait 5 minutes before executing again. 
            // 4.Repeat from point 2.
            Console.WriteLine("{0} Creating timer.\n",
                DateTime.Now.ToString("h:mm:ss.fff"));
            //Start Immediately but don't run again.
            StateTimer = new Timer(timerDelegate, AutoEventInstance, 0, Timeout.Infinite);
            while (StateTimer != null)
            {
                //Wait until the job is done
                AutoEventInstance.WaitOne();
                //Wait for 5 minutes before starting the job again.
                StateTimer.Change(TimerInterval, Timeout.Infinite);
            }
            //If the Job somehow takes longer than 5 minutes to complete then it wont matter because we will always wait another 5 minutes before running again.
        }

        protected override void OnStop()
        {
            StateTimer.Dispose();
        }
    }

    class StatusChecker
        {

            public StatusChecker()
            {
            }

            // This method is called by the timer delegate.
            public void CheckStatus(Object stateInfo)
            {
                AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
                Console.WriteLine("{0} Start Checking status.",
                    DateTime.Now.ToString("h:mm:ss.fff"));
                //This job takes time to run. For example purposes, I put a delay in here.
                int milliseconds = 5000;
                Thread.Sleep(milliseconds);
                //Job is now done running and the timer can now be reset to wait for the next interval
                Console.WriteLine("{0} Done Checking status.",
                    DateTime.Now.ToString("h:mm:ss.fff"));
                autoEvent.Set();
            }
        }
MigaelM
sumber