Làm cách nào để lên lịch cho Dịch vụ C # Windows để thực hiện một tác vụ hàng ngày?


84

Tôi có một dịch vụ được viết bằng C # (.NET 1.1) và muốn nó thực hiện một số hành động dọn dẹp vào lúc nửa đêm hàng đêm. Tôi phải giữ tất cả mã có trong dịch vụ, vậy cách dễ nhất để thực hiện điều này là gì? Sử dụng Thread.Sleep()và kiểm tra thời gian trôi qua?


10
Bạn thực sự muốn tạo một quy trình .NET nằm trong bộ nhớ, cả ngày và thực hiện điều gì đó vào lúc nửa đêm? Chính xác là tại sao bạn không thể, khi cài đặt công cụ của mình, thiết lập một sự kiện hệ thống kích hoạt vào lúc nửa đêm (hoặc một số thời gian có thể định cấu hình khác) để khởi động ứng dụng của bạn, sau đó biến mất?
Robert P

6
Tôi biết mình sẽ hơi bực mình nếu phát hiện ra mình có một dịch vụ được quản lý tiêu thụ 20-40 megs bộ nhớ, chạy mọi lúc, không hoạt động gì ngoại trừ một lần mỗi ngày. :)
Robert P

Câu trả lời:


86

Tôi sẽ không sử dụng Thread.Sleep (). Sử dụng một tác vụ đã lên lịch (như những người khác đã đề cập) hoặc thiết lập bộ hẹn giờ bên trong dịch vụ của bạn, bộ hẹn giờ sẽ kích hoạt định kỳ (ví dụ: 10 phút một lần) và kiểm tra xem ngày có thay đổi kể từ lần chạy cuối cùng hay không:

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
Sẽ không hợp lý hơn nếu chỉ đặt bộ hẹn giờ hết hạn vào lúc nửa đêm, thay vì thức dậy mỗi phút để kiểm tra thời gian?
Kibbee

11
@Kibbee: có thể nếu dịch vụ không chạy vào lúc nửa đêm (vì lý do gì đó), thì bạn có thể muốn thực thi tác vụ ngay khi dịch vụ đang chạy lại.
M4N

4
Sau đó, bạn nên thêm một số mã khi khởi động dịch vụ để tìm hiểu xem bạn có nên chạy ngay lập tức hay không, vì dịch vụ không chạy và nó nên như vậy. Bạn không nên kiểm tra 10 phút một lần liên tục. Kiểm tra xem bạn có nên chạy khi khởi động hay không, sau đó nếu không, hãy tìm ra lần chạy tiếp theo. sau đó, và đặt hẹn giờ trong khoảng thời gian bạn cần.
Kibbee

3
@Kibbee: vâng, tôi đồng ý (đó là một chi tiết nhỏ mà chúng tôi đang thảo luận). Nhưng ngay cả khi bộ hẹn giờ kích hoạt sau mỗi 10 giây, nó sẽ không gây hại gì (tức là không tốn thời gian).
M4N

1
giải pháp tốt nhưng mã ở trên bị thiếu _timer.start () và _lastRun nên được đặt thành ngày hôm qua như sau: private DateTime _lastRun = DateTime.Now.AddDays (-1);
Zulu Z

72

Kiểm tra Quartz.NET . Bạn có thể sử dụng nó trong một dịch vụ Windows. Nó cho phép bạn chạy một công việc dựa trên một lịch trình đã định cấu hình, và nó thậm chí còn hỗ trợ một cú pháp đơn giản "cron job". Tôi đã có rất nhiều thành công với nó.

Dưới đây là một ví dụ nhanh về cách sử dụng của nó:

// 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
Một gợi ý hay - ngay sau khi bạn làm điều gì đó thậm chí phức tạp hơn một chút so với những thứ cơ bản của Bộ đếm thời gian, bạn nên xem xét Quartz.
serg10

2
Quartz rất dễ sử dụng, tôi cho rằng ngay cả đối với một nhiệm vụ siêu đơn giản, chỉ cần tiếp tục và sử dụng nó. Nó sẽ cung cấp khả năng dễ dàng điều chỉnh lịch trình.
Andy White

Siêu đơn giản phải không? Hầu hết những người mới sẽ gặp sự cố này ở đây stackoverflow.com/questions/24628372/…
Emil

21

Một nhiệm vụ hàng ngày? Có vẻ như nó chỉ nên là một tác vụ đã lên lịch (bảng điều khiển) - không cần dịch vụ ở đây.


15

Nó có phải là một dịch vụ thực tế không? Bạn có thể sử dụng các tác vụ đã lên lịch sẵn trong bảng điều khiển windows không.


Vì dịch vụ đã tồn tại, nên việc thêm chức năng vào đó có thể dễ dàng hơn.
M4N

1
Việc chuyển đổi một dịch vụ có mục đích hẹp chẳng hạn như dịch vụ này thành một ứng dụng bảng điều khiển sẽ là một việc nhỏ.
Stephen Martin

7
Anh ấy đã nói, "Tôi phải giữ tất cả mã có trong dịch vụ". Tôi không cảm thấy cần thiết phải đặt câu hỏi về các yêu cầu ban đầu. Nó phụ thuộc, nhưng đôi khi có những lý do rất tốt để sử dụng một dịch vụ thay vì một nhiệm vụ đã lên lịch.
jeremcc 02/02/09

3
+1: Bởi vì bạn luôn cần nhìn nhận vấn đề của mình từ mọi phía.
GvS

6
Nếu tôi thực sự chỉ có một nhiệm vụ phải chạy mỗi ngày, thì tôi đoán một ứng dụng console đơn giản chạy với một tác vụ đã lên lịch sẽ ổn. Quan điểm của tôi là tất cả phụ thuộc vào tình huống và nói "Không sử dụng dịch vụ windows" không phải là một câu trả lời hữu ích IMO.
jeremcc 02/02/09

3

Cách tôi thực hiện điều này là với bộ đếm thời gian.

Chạy bộ hẹn giờ máy chủ, để nó kiểm tra Giờ / phút sau mỗi 60 giây.

Nếu đó là Giờ / phút phù hợp, hãy chạy quy trình của bạn.

Tôi thực sự đã trừu tượng hóa điều này thành một lớp cơ sở mà tôi gọi là OnceADayRunner.

Hãy để tôi xóa mã một chút và tôi sẽ đăng nó ở đây.

    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();
        }
    }

Thịt bò của phương pháp là trong kiểm tra e.SignalTime.Minute / Hour.

Có các móc trong đó để thử nghiệm, v.v. nhưng đây là những gì bộ hẹn giờ đã trôi qua của bạn có thể trông như thế nào để làm cho tất cả hoạt động.


Một chút chậm trễ do máy chủ bận có thể khiến máy chủ bỏ lỡ giờ + phút.
Jon B

1
Thật. Khi tôi làm điều này, tôi đã kiểm tra: Hiện tại có phải là thời gian lớn hơn 00:00 không? Nếu vậy, đã hơn 24 giờ kể từ lần cuối tôi chạy công việc? Nếu vậy, hãy chạy công việc, nếu không, hãy đợi lại.
ZombieSheep

2

Như những người khác đã viết, hẹn giờ là lựa chọn tốt nhất trong trường hợp bạn đã mô tả.

Tùy thuộc vào yêu cầu chính xác của bạn, việc kiểm tra thời gian hiện tại mỗi phút có thể không cần thiết. Nếu bạn không cần thực hiện hành động chính xác vào lúc nửa đêm, nhưng chỉ trong vòng một giờ sau nửa đêm, bạn có thể thực hiện phương pháp của Martin là chỉ kiểm tra xem ngày đã thay đổi hay chưa.

Nếu lý do bạn muốn thực hiện hành động của mình lúc nửa đêm là do bạn mong đợi khối lượng công việc trên máy tính của mình thấp, hãy cẩn thận hơn: Giả định tương tự thường được đưa ra bởi những người khác và đột nhiên bạn có 100 hành động dọn dẹp bắt đầu từ 0:00 đến 0 : 01 giờ sáng

Trong trường hợp đó, bạn nên cân nhắc việc bắt đầu dọn dẹp của mình vào một thời điểm khác. Tôi thường làm những việc đó không phải vào giờ đồng hồ mà là vào nửa giờ (1.30 sáng là sở thích cá nhân của tôi)


1

Tôi khuyên bạn nên sử dụng bộ đếm thời gian, nhưng hãy đặt nó kiểm tra sau mỗi 45 giây, không phải phút. Nếu không, bạn có thể gặp phải các tình huống khi tải nặng, việc kiểm tra một phút cụ thể bị bỏ lỡ, bởi vì giữa thời gian bộ hẹn giờ kích hoạt và thời gian mã của bạn chạy và kiểm tra thời gian hiện tại, bạn có thể đã bỏ lỡ phút mục tiêu.


Nếu bạn không kiểm tra để đảm bảo rằng nó chưa chạy một lần, có khả năng bạn đạt 00:00 hai lần nếu bạn đang kiểm tra 45 giây.
Michael Meadows

Điểm tốt. Có thể tránh được sự cố này bằng cách gọi Sleep (15000) ở cuối quy trình kiểm tra. (hoặc có lẽ là 16000 mili giây, chỉ để ở bên an toàn ;-)
Treb

Tôi đồng ý, bạn nên kiểm tra xem nó đã chạy chưa, bất kể bạn chọn thời lượng hẹn giờ nào.
GWLlosa 02/02/09


0

Đối với những giải pháp tìm thấy các giải pháp trên không hoạt động, đó là bởi vì bạn có thể có một thisbên trong lớp của mình, ngụ ý một phương thức mở rộng, như thông báo lỗi cho biết, chỉ có ý nghĩa trên một lớp tĩnh không chung chung. Lớp học của bạn không tĩnh. Đây dường như không phải là một cái gì đó có ý nghĩa như một phương thức mở rộng, vì nó hoạt động trên trường hợp được đề cập, vì vậy hãy loại bỏ this.


-2

Thử đi:

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
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.