簡體   English   中英

為每個實例創建一個計時器有什么問題嗎?

[英]Is there anything wrong with creating a timer for each instance?

為了更好地理解,我將以DHCP租約的簡單抽象為例:租約包含IP和MAC地址,它被授予的時間,並且可以在給定的時間跨度內更新。 一旦過期,將調用一個事件。 同樣,這只是我能想到的最小的例子:

using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Timers;

namespace Example
{
    public class Lease
    {
        public IPAddress IP
        {
            get;
            private set;
        }

        public PhysicalAddress MAC
        {
            get;
            private set;
        }

        public DateTime Granted
        {
            get;
            private set;
        }

        public event EventHandler Expired;

        private readonly Timer timer;

        public Lease(IPAddress ip, PhysicalAddress mac, TimeSpan available)
        {
            IP = ip;
            MAC = mac;

            timer = new Timer();
            timer.AutoReset = false;
            timer.Elapsed += timerElapsed;

            Renew(available);
        }

        public void timerElapsed(object sender, EventArgs e)
        {
            var handle = Expired;
            if (handle != null)
            {
                handle(this, EventArgs.Empty);
            }
        }

        public void Renew(TimeSpan available)
        {
            Granted = DateTime.Now;
            timer.Interval = available.TotalMilliseconds;
            timer.Enabled = true;
        }
    }
}

創建時有什么需要考慮的 - 例如 - 這類課程的“幾千個”實例嗎? 我最關心的是計時器。 我是否應該為這樣的任務考慮另一種設計模式 (比如所有租約的經理,或者根本不使用計時器?) 或者在創建大量計時器時沒有什么可擔心的,這是合適的方法嗎? 至少我總是試圖在計時器和事件時保持謹慎。

您可以只存儲每個Lease對象的到期時間,而不是創建數千個計時器,然后定期在單個線程查詢中查找已過期的對象。

在我的頭部代碼示例的頂部:

var leases = new List<Lease>();
var running = true;

var expiredChecker = Task.Factory.StartNew(() =>
{
    while (running)
    {
        var expired = leases.All(l => l.ExpirationDate < DateTime.Now);
        // do something with the expired lease objects
    }
});

假設您的Lease對象上有一個IEnumerable<Lease> ,一個名為ExpirationDateDateTime屬性,則可以通過在要停止時將運行設置為false來取消它。

根據System.Timers.Timer MSDN頁面

基於服務器的Timer設計用於多線程環境中的工作線程。 服務器計時器可以在線程之間移動以處理引發的Elapsed事件,從而在准時引發事件時比Windows計時器更准確。

這意味着當您同時運行幾千個計時器時,它不太可能導致問題。

這並不意味着它是一個好方法,你應該尋找一個更集中的解決方案來解決這個問題。

我建議使用System.Threading.Timer而不是System.Timers.Timer 第二個是關於第一個在設計時可見的包裝器,如果你真的不需要設計時間支持則沒有必要。 Timer內部調用ThreadPool.QueueUseWorkItem ,而線程池負責維護計時器tick上的線程。 線程池只使用一個線程來維護所有的定時器對象,並且該線程決定每個定時器隊列中的新線程何時打開。

我不能看到任何開銷,除非你的計時器打得如此之快,以至於你無法完成所有的工作,而你只是在線程池中排隊太多工作。

我認為這部分取決於您在服務器上可用的資源,以及您需要的准確性和性能。

另一種方法可能是在每個實例中存儲像時間戳一樣簡單的東西,並定期檢查該值,將其與當前時間進行比較,並適當地更新它。 我有預感,這可能會更容易在性能上 - 但你應該嘗試以某種方式對它進行基准測試以確保。

當然,如果你有大量的實例,迭代所有實例也可能需要一些時間,所以可能將這些組合成組,其中每個組在常規(可調節?)間隔的單獨線程中處理可能是一個選項。

如果沒有關於性能的一些信息,在這里給出一個很好的答案有點困難,所以你應該只是創建一個概念證明,並測試你認為可能有效的幾個策略,並嘗試對它們進行基准測試,看看哪個最合適。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM