2011-11-22 22 views
19

Tôi đang sử dụng System.Timers.Timer trong ứng dụng WPF của mình. Tôi muốn hiểu làm thế nào Timer hành xử, sau khi máy tính là ngủ đông, và ngủ. Tôi nhận được một số vấn đề lạ với ứng dụng của mình, sau khi máy tính được khôi phục từ chế độ ngủ đông.Cách System.Timers.Timer hoạt động trong ứng dụng WPF, sau Hibernate và Sleep?

Tôi nên xử lý bộ hẹn giờ như thế nào và chúng hoạt động như thế nào khi máy tính ở chế độ ngủ đông/ngủ đông?

Tôi có bộ hẹn giờ nửa đêm sẽ hoạt động sau nửa đêm để đặt lại giá trị mặc định trên giao diện người dùng.

Đây là mã tạo bộ đếm thời gian:

private void ResetMidnightTimer() 
     { 
      // kill the old timer 
      DisposeMidnightTimer(); 

      _midnightTimer = new Timer(); 
      // scheduling the timer to elapse 1 minute after midnight 
      _midnightTimer.Interval = (DateTime.Today.AddDays(1).AddMinutes(1) - DateTime.Now).TotalMilliseconds; 
      _midnightTimer.Elapsed += (_, __) => UpdateRecommendedCollectingTime(); 
      _midnightTimer.Enabled = true; 
      _midnightTimer.Start(); 
     } 

On contructor trang UI, tôi gọi phương thức trong đó kêu gọi ResestMidnightTimer() và tạo ra bộ đếm thời gian de facto. Sau đó, bộ đếm thời gian chờ đợi ban đêm.

Khi thời gian ban đêm (thực sự là 12:01 sáng), bộ hẹn giờ hoạt động, đặt lại giá trị mặc định như mong đợi và sau đó hủy hẹn giờ hiện có. Cuối cùng nó tạo ra một bộ đếm thời gian nửa đêm mới cho ngày hôm sau. Nhưng nếu tôi cố gắng ngủ đông máy tính trong ngày đó, bộ hẹn giờ nửa đêm sẽ không hoạt động và sẽ không đặt lại các giá trị mặc định.

Đó có phải là vì trong khi ngủ đông nó chỉ trì hoãn việc xử lý sự kiện theo cùng một khoảng thời gian nó được ngủ đông không?

+6

Bạn đang gặp phải vấn đề gì (chỉ tò mò)? – M4N

+0

@ M4N Tôi đã chỉnh sửa câu hỏi. – User1234

Trả lời

14

Điều này tùy thuộc vào cách bạn đang sử dụng bộ hẹn giờ của mình. Nếu bạn đang sử dụng chúng để bắt đầu một số sự kiện xảy ra không thường xuyên (lớn hơn một vài phút) sau đó bạn có thể sẽ thấy một số hành vi 'lạ'. Vì bạn không chỉ định hành vi 'lạ' đó là gì, tôi sẽ giả định rằng bộ hẹn giờ của chương trình sẽ tắt muộn hơn mức cần thiết.

Giải thích: Sự cố khi chuyển sang chế độ ngủ/ngủ đông là tất cả các chương trình đều bị tạm ngưng. Điều này có nghĩa là Timers của bạn không được cập nhật và do đó khi bạn ngủ/ngủ đông và quay trở lại, nó giống như bạn bị đóng băng trong khoảng thời gian mà bạn đang ngủ/ngủ đông. Điều này có nghĩa là nếu bạn có bộ hẹn giờ tắt trong một giờ và máy tính của bạn chuyển sang chế độ ngủ ở mốc 15 phút, sau khi nó thức dậy, sẽ có thêm 45 phút nữa, bất kể máy tính đang ngủ bao lâu.

Giải pháp: Một lần sửa chữa sẽ là giữ DateTime trong khoảng thời gian gần nhất xảy ra sự kiện. Sau đó, có hẹn giờ tắt định kỳ (cứ 10 giây hoặc 10 phút, tùy thuộc vào độ chính xác mong muốn) và kiểm tra DateTime của lần thực thi cuối cùng. Nếu sự khác biệt giữa bây giờ và thời gian thực hiện cuối cùng lớn hơn hoặc bằng khoảng thời gian mong muốn, THÌ bạn chạy thực thi.

Điều này sẽ khắc phục sự cố sao cho nếu một sự kiện 'phải có' xảy ra trong khi ngủ/ngủ đông, nó sẽ bắt đầu ngay khi bạn trở về sau khi ngủ/ngủ đông.

Cập nhật: Giải pháp được trình bày ở trên sẽ hoạt động và tôi sẽ điền một vài chi tiết để giúp bạn triển khai.

  • Thay vì tạo/xử lý Timers mới, tạo ONE hẹn giờ để sử dụng được định kỳ (tài sản AutoReset được thiết lập là true)

  • Khoảng cách của bộ đếm thời gian đơn nên NOT được đặt theo thời gian tiếp theo sự kiện sẽ xảy ra. Thay vào đó, nó phải được đặt thành giá trị bạn chọn sẽ đại diện cho tần suất bỏ phiếu (tần suất nó kiểm tra xem liệu 'sự kiện' có nên chạy) hay không. Sự lựa chọn nên là một sự cân bằng về hiệu quả và độ chính xác. Nếu bạn CẦN nó chạy REALLY gần 12:01 AM thì bạn đặt khoảng thời gian khoảng 5-10 giây. Nếu nó ít quan trọng hơn vào lúc 12:01 sáng, bạn có thể tăng khoảng thời gian lên khoảng 1-10 phút.

  • Bạn cần giữ khoảng thời gian DateTime khi thực hiện lần cuối xảy ra HOẶC khi thực hiện tiếp theo sẽ xảy ra. Tôi muốn 'khi thực hiện tiếp theo sẽ xảy ra' để bạn không làm (LastExecutionTime + EventInterval) mỗi khi bộ đếm thời gian trôi qua, bạn sẽ chỉ so sánh thời gian hiện tại và thời gian sự kiện sẽ xảy ra.

  • Khi bộ hẹn giờ hết hạn và sự kiện NÊN xảy ra (ở đâu đó khoảng 12:01 AM), bạn nên cập nhật DateTime đã lưu và sau đó chạy mã bạn muốn chạy lúc 12:01 sáng.

Ngủ vs Hibernate Làm rõ: Sự khác biệt chính giữa giấc ngủ và ngủ đông là trong giấc ngủ, tất cả mọi thứ được giữ trong RAM trong khi ngủ đông tiết kiệm tình trạng hiện thời vào đĩa. Ưu điểm chính của hibernate là RAM không còn cần nguồn điện và do đó tiết kiệm ít năng lượng hơn. Đây là lý do tại sao nó được khuyến khích để sử dụng ngủ đông trên ngủ khi giao dịch với máy tính xách tay hoặc các thiết bị khác bằng cách sử dụng một số lượng hữu hạn của năng lượng.

Điều đó nói rằng, không có sự khác biệt trong việc thực hiện các chương trình khi chúng bị tạm ngưng trong cả hai trường hợp. Thật không may, System.Timers.Timer không 'đánh thức' máy tính và do đó bạn không thể thực thi mã của mình để chạy lúc ~ 12: 01 sáng.

Tôi tin rằng có nhiều cách khác để 'đánh thức' máy tính nhưng trừ khi bạn đi tuyến đường tốt nhất bạn có thể làm là chạy 'sự kiện' trong 'sự kiện bỏ phiếu' tiếp theo của bộ hẹn giờ sau khi hết giờ/hibernate.

+0

+1 để kiểm tra đề xuất của tiểu bang – Unsliced

+0

+1 để kiểm tra chênh lệch thời gian ngày. – PRR

+0

@docmanhattan Cảm ơn rất nhiều !!!! Tôi đã chỉnh sửa Câu hỏi và thêm các chi tiết khác vào câu hỏi đó. Những gì bạn nghĩ rằng bạn sẽ giải pháp làm việc trong trường hợp này? – User1234

1

System.Timers.Timer là bộ đếm thời gian dựa trên máy chủ (sự kiện đã trôi qua sử dụng Threadpool. Chính xác hơn các bộ hẹn giờ khác). Khi máy tính của bạn ở chế độ ngủ hoặc ở chế độ ngủ đông, tất cả trạng thái chương trình của bạn sẽ được lưu trong RAM. Cũng vậy với trạng thái ứng dụng của bạn. Sau khi hệ thống của bạn khởi động trạng thái ứng dụng của bạn sẽ được hệ điều hành khôi phục (cùng với bộ hẹn giờ). Nó sẽ không là một ý tưởng tốt để "làm một cái gì đó" hoặc cố gắng để phát hiện sự kiện này. Nó có thể từ một dịch vụ Windows mặc dù. Hãy để nó vào hệ điều hành để thực hiện công việc của mình.

3

Đó có phải là vì trong khi ngủ đông nó chỉ trì hoãn việc xử lý sự kiện bằng cùng một khoảng thời gian được ngủ đông không?

Trong khi máy tính ở chế độ tạm ngưng (ví dụ: ngủ hoặc ngủ đông), nó không làm gì cả. Điều này bao gồm, đặc biệt, lịch trình xử lý việc đánh thức luồng đang theo dõi hàng đợi của các sự kiện hẹn giờ không chạy và do đó luồng không thực hiện bất kỳ tiến trình nào để tiếp tục thực hiện để xử lý bộ hẹn giờ tiếp theo.

Không quá nhiều sự kiện rõ ràng là bị hoãn mỗi lần. Nhưng có, đó là hiệu ứng ròng.

Trong một số trường hợp, có thể sử dụng lớp hẹn giờ không có vấn đề này. Cả hai System.Windows.Forms.TimerSystem.Windows.Threading.DispatcherTimer đều không dựa trên bộ lập lịch trình chuỗi Windows, mà thay vào đó là thông báo WM_TIMER. Bởi vì cách thông báo này hoạt động — nó được tạo ra "trên bay" khi vòng lặp tin nhắn của một chuỗi kiểm tra hàng đợi tin nhắn, dựa trên thời gian hết hạn cho bộ đếm thời gian đã trôi qua & hellip, theo cách nào đó, nó tương tự như việc bỏ phiếu xung quanh được mô tả trong the other answer to your question — nó miễn dịch với sự chậm trễ nếu không sẽ do máy tính bị treo.

Bạn đã tuyên bố kịch bản của mình liên quan đến chương trình WPF, vì vậy bạn có thể thấy rằng giải pháp tốt nhất của bạn thực sự là sử dụng lớp DispatcherTimer, thay vì System.Timers.Timer.

Nếu bạn quyết định bạn cần triển khai hẹn giờ mà không được gắn với thread UI, đây là một phiên bản của System.Threading.Timer đó một cách chính xác sẽ tính đến thời gian chi tiêu tài khoản trong khi lơ lửng:

class SleepAwareTimer : IDisposable 
{ 
    private readonly Timer _timer; 
    private TimeSpan _dueTime; 
    private TimeSpan _period; 
    private DateTime _nextTick; 
    private bool _resuming; 

    public SleepAwareTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period) 
    { 
     _dueTime = dueTime; 
     _period = period; 
     _nextTick = DateTime.UtcNow + dueTime; 
     SystemEvents.PowerModeChanged += _OnPowerModeChanged; 

     _timer = new System.Threading.Timer(o => 
     { 
      _nextTick = DateTime.UtcNow + _period; 
      if (_resuming) 
      { 
       _timer.Change(_period, _period); 
       _resuming = false; 
      } 
      callback(o); 
     }, state, dueTime, period); 
    } 

    private void _OnPowerModeChanged(object sender, PowerModeChangedEventArgs e) 
    { 
     if (e.Mode == PowerModes.Resume) 
     { 
      TimeSpan dueTime = _nextTick - DateTime.UtcNow; 

      if (dueTime < TimeSpan.Zero) 
      { 
       dueTime = TimeSpan.Zero; 
      } 

      _timer.Change(dueTime, _period); 
      _resuming = true; 
     } 
    } 

    public void Change(TimeSpan dueTime, TimeSpan period) 
    { 
     _dueTime = dueTime; 
     _period = period; 
     _nextTick = DateTime.UtcNow + _dueTime; 
     _resuming = false; 
     _timer.Change(dueTime, period); 
    } 

    public void Dispose() 
    { 
     SystemEvents.PowerModeChanged -= _OnPowerModeChanged; 
     _timer.Dispose(); 
    } 
} 

Giao diện công cộng cho System.Threading.Timer và giao diện tập hợp con ở trên được sao chép từ lớp đó, khác với những gì bạn sẽ tìm thấy trên System.Timers.Timer, nhưng nó hoàn thành điều tương tự. Nếu bạn thực sự muốn một lớp học hoạt động chính xác như System.Timers.Timer, nó không phải là khó khăn để thích ứng với các kỹ thuật trên cho phù hợp với nhu cầu của bạn.

Các vấn đề liên quan