Günlük bir görevi gerçekleştirmek için bir C # Windows Hizmetini nasıl planlayabilirim?


84

C # (.NET 1.1) ile yazılmış bir hizmetim var ve her gece gece yarısı bazı temizleme eylemleri gerçekleştirmesini istiyorum. Hizmetin içerdiği tüm kodu saklamam gerekiyor, bu yüzden bunu gerçekleştirmenin en kolay yolu nedir? Kullanımı Thread.Sleep()ve üzerinde haddeleme defa kontrol?


10
Gerçekten bütün gün bellekte duran ve gece yarısı bir şeyler yapan bir .NET işlemi yapmak istiyor musunuz? Aracınızı kurduktan sonra, gece yarısı (veya yapılandırılabilir başka bir zamanda) uygulamanızı başlatan, işini yapan ve sonra kaybolan bir sistem olayını neden tam olarak ayarlayamıyorsunuz?
Robert P

6
20-40 mega kadar bellek tüketen, sürekli çalışan ve günde bir kez dışında hiçbir şey yapmayan yönetilen bir hizmetim olduğunu öğrenirsem biraz sinirleneceğimi biliyorum. :)
Robert P

yinelenen bağlantısı
csa

Yanıtlar:


86

Thread.Sleep () kullanmam. Ya zamanlanmış bir görev kullanın (diğerlerinin de bahsettiği gibi) ya da hizmetinizde periyodik olarak (örneğin her 10 dakikada bir) çalışan bir zamanlayıcı ayarlayın ve son çalıştırmadan bu yana tarihin değişip değişmediğini kontrol edin:

private Timer _timer;
private DateTime _lastRun = DateTime.Now.AddDays(-1);

protected override void OnStart(string[] args)
{
    _timer = new Timer(10 * 60 * 1000); // every 10 minutes
    _timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
    _timer.Start();
    //...
}


private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // ignore the time, just compare the date
    if (_lastRun.Date < DateTime.Now.Date)
    {
        // stop the timer while we are running the cleanup task
        _timer.Stop();
        //
        // do cleanup stuff
        //
        _lastRun = DateTime.Now;
        _timer.Start();
    }
}

28
Saati kontrol etmek için her dakika uyanmak yerine zamanlayıcıyı gece yarısı sona erecek şekilde ayarlamak daha mantıklı olmaz mıydı?
Kibbee

11
@Kibbee: belki hizmet gece yarısı çalışmıyorsa (herhangi bir nedenle), hizmet tekrar çalışır çalışmaz görevi yürütmek isteyebilirsiniz.
M4N

4
Ardından, hemen koşmanız gerekip gerekmediğini anlamak için hizmetin başlangıcına bazı kodlar eklemelisiniz, çünkü hizmet çalışmıyordu ve öyle olmalı. Her 10 dakikada bir sürekli kontrol etmemelisiniz. Başlangıçta çalıştırmanız gerekip gerekmediğini kontrol edin ve ardından bir sonraki çalıştırmanız için karar veremezseniz. bundan sonra ve ihtiyacınız olan uzunlukta bir zamanlayıcı ayarlayın.
Kibbee

3
@Kibbee: evet katılıyorum (tartıştığımız küçük bir ayrıntı). Ancak zamanlayıcı her 10 saniyede bir ateşlese bile herhangi bir zarar vermez (yani zaman alıcı değildir).
M4N

1
iyi bir çözüm ancak yukarıdaki kod eksik _timer.start () ve _lastRun şu şekilde düne ayarlanmalıdır: private DateTime _lastRun = DateTime.Now.AddDays (-1);
Zulu Z

72

Quartz.NET'e göz atın . Bunu bir Windows hizmeti içinde kullanabilirsiniz. Yapılandırılmış bir programa göre bir işi çalıştırmanıza izin verir ve hatta basit bir "cron işi" sözdizimini destekler. Onunla çok başarılı oldum.

İşte kullanımının hızlı bir örneği:

// Instantiate the Quartz.NET scheduler
var schedulerFactory = new StdSchedulerFactory();
var scheduler = schedulerFactory.GetScheduler();

// Instantiate the JobDetail object passing in the type of your
// custom job class. Your class merely needs to implement a simple
// interface with a single method called "Execute".
var job = new JobDetail("job1", "group1", typeof(MyJobClass));

// Instantiate a trigger using the basic cron syntax.
// This tells it to run at 1AM every Monday - Friday.
var trigger = new CronTrigger(
    "trigger1", "group1", "job1", "group1", "0 0 1 ? * MON-FRI");

// Add the job to the scheduler
scheduler.AddJob(job, true);
scheduler.ScheduleJob(trigger);

2
İyi bir öneri - temel Zamanlayıcı şeyinden biraz daha karmaşık bir şey yaptığınız anda Quartz'a bakmalısınız.
serg10

2
Quartz'ın kullanımı o kadar kolay ki, süper basit bir görev için bile devam edin ve kullanın. Programlamayı kolayca ayarlama yeteneği verecektir.
Andy White

Süper basit gerçekten değil mi? Yeni başlayanların çoğu bu sorunu burada yakalayacak stackoverflow.com/questions/24628372/…
Emil

21

Günlük bir görev mi? Görünüşe göre sadece zamanlanmış bir görev (kontrol paneli) - burada bir servise gerek yok.


15

Gerçek bir hizmet olmak zorunda mı? Windows kontrol panelindeki yerleşik zamanlanmış görevleri kullanabilir misiniz?


Hizmet zaten mevcut olduğundan, işlevselliği oraya eklemek daha kolay olabilir.
M4N

1
Bunun gibi dar amaçlı bir hizmeti bir konsol uygulamasına dönüştürmek önemsiz olmalıdır.
Stephen Martin

7
Zaten "hizmetin içerdiği tüm kodu saklamam gerekiyor" dedi. Orijinal gereksinimleri sorgulamanın gerekli olduğunu düşünmüyorum. Duruma göre değişir, ancak bazen bir hizmeti zamanlanmış bir görev yerine kullanmak için çok iyi nedenler olabilir.
jeremcc

3
+1: Çünkü sorunlarınıza her zaman her yönden bakmanız gerekiyor.
GvS

6
Gerçekten günde çalıştıracak tek bir görevim olsaydı , sanırım planlanmış bir görevle çalıştırılan basit bir konsol uygulaması iyi olurdu. Demek istediğim, her şeyin duruma bağlı olması ve "Windows hizmeti kullanma" demek yararlı bir yanıt değildir IMO.
jeremcc

3

Bunu başarma şeklim bir zamanlayıcı ile.

Bir sunucu zamanlayıcı çalıştırın, her 60 saniyede bir Saati / Dakikayı kontrol etmesini sağlayın.

Doğru Saat / Dakika ise, işleminizi çalıştırın.

Aslında bunu OnceADayRunner olarak adlandırdığım bir temel sınıfa soyutladım.

Kodu biraz temizleyeyim ve buraya göndereceğim.

    private void OnceADayRunnerTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        using (NDC.Push(GetType().Name))
        {
            try
            {
                log.DebugFormat("Checking if it's time to process at: {0}", e.SignalTime);
                log.DebugFormat("IsTestMode: {0}", IsTestMode);

                if ((e.SignalTime.Minute == MinuteToCheck && e.SignalTime.Hour == HourToCheck) || IsTestMode)
                {
                    log.InfoFormat("Processing at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
                    OnceADayTimer.Enabled = false;
                    OnceADayMethod();
                    OnceADayTimer.Enabled = true;

                    IsTestMode = false;
                }
                else
                {
                    log.DebugFormat("Not correct time at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
                }
            }
            catch (Exception ex)
            {
                OnceADayTimer.Enabled = true;
                log.Error(ex.ToString());
            }

            OnceADayTimer.Start();
        }
    }

Yöntemin sığır eti e.SinyalSüresi Dakika / Saat kontrolündedir.

Orada test vb. İçin kancalar var, ancak bu, tümünün çalışmasını sağlamak için geçen zamanlayıcınız gibi görünebilir.


Meşgul bir sunucudan kaynaklanan küçük bir gecikme, saat + dakikayı kaçırmasına neden olabilir.
Jon B

1
Doğru. Bunu yaptığımda kontrol ettim: Şimdi saat 00: 00'dan büyük mü? Öyleyse, işi son çalıştırmamın üzerinden 24 saatten fazla mı geçti? Eğer öyleyse, işi çalıştırın, yoksa tekrar bekleyin.
ZombieSheep

2

Diğerlerinin zaten yazdığı gibi, anlattığınız senaryoda bir zamanlayıcı en iyi seçenektir.

Tam gereksinimlerinize bağlı olarak, her dakika geçerli saati kontrol etmek gerekli olmayabilir. Eylemi tam olarak gece yarısında gerçekleştirmeniz gerekmiyorsa , ancak gece yarısından sonraki bir saat içinde, Martin'in yalnızca tarihin değişip değişmediğini kontrol etme yaklaşımına gidebilirsiniz .

Eyleminizi gece yarısı gerçekleştirmek istemenizin nedeni bilgisayarınızda düşük bir iş yükü beklemenizse, daha iyi dikkatli olun: Aynı varsayım genellikle başkaları tarafından yapılır ve aniden 0:00 ile 0 arasında 100 temizleme eylemi başlar. : 01

Bu durumda, temizliğe farklı bir zamanda başlamayı düşünmelisiniz. Bunları genellikle saat başında değil, yarım saatlerde yapıyorum (kişisel tercihim saat 1.30'um)


1

Bir zamanlayıcı kullanmanızı öneririm, ancak bunu dakikada değil, 45 saniyede bir kontrol edecek şekilde ayarlayın. Aksi takdirde, ağır yükle belirli bir dakika için kontrolün kaçırıldığı durumlarla karşılaşabilirsiniz, çünkü zamanlayıcının tetiklendiği zaman ile kodunuzun çalıştığı ve geçerli saati kontrol ettiği zaman arasında, hedef dakikayı kaçırmış olabilirsiniz.


Zaten bir kez çalışmadığından emin olmak için kontrol etmiyorsanız, 45 saniyeyi kontrol ediyorsanız 00: 00'a iki kez basma potansiyeli var.
Michael Meadows

İyi bir nokta. Bu problem, kontrol prosedürünün sonunda Uyku (15000) aranarak önlenebilir. (veya belki 16000 ms, sadece güvenli tarafta olmak için ;-)
Treb

Kabul ediyorum, seçtiğiniz zamanlayıcı süresi ne olursa olsun, zaten çalışıp çalışmadığını kontrol etmelisiniz.
GWLlosa


0

Yukarıdaki çözümlerin işe yaramadığını bulanlar için, bunun nedeni this, sınıfınızın içinde bir içeriğe sahip olabilmenizdir; bu, hata mesajının da belirttiği gibi, yalnızca genel olmayan bir statik sınıfta anlamlı olan bir uzantı yöntemini ifade eder. Sınıfınız statik değil. Bu, söz konusu örnekte hareket ettiği için bir uzantı yöntemi olarak mantıklı görünmüyor, bu nedenle this.


-2

Bunu dene:

public partial class Service : ServiceBase
{
    private Timer timer;
    public Service()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        SetTimer();
    }

    private void SetTimer()
    {
        if (timer == null)
        {
            timer = new Timer();
            timer.AutoReset = true;
            timer.Interval = 60000 * Convert.ToDouble(ConfigurationManager.AppSettings["IntervalMinutes"]);
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.Start();
        }
    }

    private void timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
    {
        //Do some thing logic here
    }

    protected override void OnStop()
    {
        // disposed all service objects
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.