哪个是使用 Stop/Start timer vs SynchronizingObject vs lock for System.Timers.Timer?

Which is to use Stop/Start timer vs SynchronizingObject vs lock for System.Timers.Timer?

最重要的是:根据评论,我猜想所有的解决方案都是有效的 事实上,在我的应用程序中,我正在尝试将它们结合起来。顺便说一下,在投票之前你可以问我详细信息。

Top note 2: 顺便说一下,由于 SO 不同于 MSO downvotes 或 close votes 鼓励删除问题,我不会,否则所有有价值的评论和答案都将被删除。这是一个互相帮助和互相理解的地方

这是 linqpad 代码的 4 种不同实现中最基本的。除了首先所有其他人都提供所需的输出。

你能为他们解释一下细节吗?

因为我的应用程序中有很多计时器,所以我需要管理和同步,以最好使用的完整代码以及替代解决方案的 pro/cons 是什么

没有 SynchronizingObject 也没有定时器 stop/start 也没有锁

System.Timers.Timer timer2 = new System.Timers.Timer(100);
int i = 0;
void Main()
{
    timer2.Elapsed += PromptForSave;
    timer2.Start();
}

private void PromptForSave(Object source, System.Timers.ElapsedEventArgs e)
{
    i = i + 1;
    Thread.Sleep(new Random().Next(100, 1000));
    Console.WriteLine(i);
}

给出:

4 5 6 7 8 9 11 12 13 14 15 15 15 17 18 20 21 22


使用同步对象:

void Main()
{
    timer2.Elapsed += PromptForSave;
    timer2.SynchronizingObject = new Synchronizer();
    timer2.Start();
}

private void PromptForSave(Object source, System.Timers.ElapsedEventArgs e)
{
    i = i + 1;
    Thread.Sleep(new Random().Next(100, 1000));
    Console.WriteLine(i);
}

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20


带定时器Start/Stop

void Main()
{
    timer2.Elapsed += PromptForSave;    
    timer2.Start();
}

private void PromptForSave(Object source, System.Timers.ElapsedEventArgs e)
{
    timer2.Stop();
    i = i + 1;
    Thread.Sleep(new Random().Next(100, 1000));
    Console.WriteLine(i);
    timer2.Start();
}

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20


终于锁上了

object lockForTimer = new object();
void Main()
{
    timer2.Elapsed += PromptForSave;    
    timer2.Start();
}

private void PromptForSave(Object source, System.Timers.ElapsedEventArgs e)
{
    lock(lockForTimer){
        i = i + 1;
        Thread.Sleep(new Random().Next(100, 1000));
        Console.WriteLine(i);
        timer2.Start();
    }
}

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

同步器看起来像:

public class Synchronizer : ISynchronizeInvoke
{
    private Thread m_Thread;
    private BlockingCollection<Message> m_Queue = new BlockingCollection<Message>();

    public Synchronizer()
    {
        m_Thread = new Thread(Run);
        m_Thread.IsBackground = true;
        m_Thread.Start();
    }

    private void Run()
    {
        while (true)
        {
            Message message = m_Queue.Take();
            message.Return = message.Method.DynamicInvoke(message.Args);
            message.Finished.Set();
        }
    }

    public IAsyncResult BeginInvoke(Delegate method, object[] args)
    {
        Message message = new Message();
        message.Method = method;
        message.Args = args;
        m_Queue.Add(message);
        return message;
    }

    public object EndInvoke(IAsyncResult result)
    {
        Message message = result as Message;
        if (message != null)
        {
            message.Finished.WaitOne();
            return message.Return;
        }
        throw new ArgumentException("result");
    }

    public object Invoke(Delegate method, object[] args)
    {
        Message message = new Message();
        message.Method = method;
        message.Args = args;
        m_Queue.Add(message);
        message.Finished.WaitOne();
        return message.Return;
    }

    public bool InvokeRequired
    {
        get { return Thread.CurrentThread != m_Thread; }
    }

    private class Message : IAsyncResult
    {
        public Delegate Method = null;
        public object[] Args = null;
        public object Return = null;
        public object State = null;
        public ManualResetEvent Finished = new ManualResetEvent(false);

        public object AsyncState
        {
            get { return State; }
        }

        public WaitHandle AsyncWaitHandle
        {
            get { return Finished; }
        }

        public bool CompletedSynchronously
        {
            get { return false; }
        }

        public bool IsCompleted
        {
            get { return Finished.WaitOne(0); }
        }
    }
}
  • 第 4 种解决方案 (lock) 是一种危险的解决方案。由于 Elapsed 事件每次都会在 ThreadPool 线程上引发,并且您可能会同时阻塞其中的许多线程,这可能会导致 ThreadPool 增长(带来所有后果)。所以这个解决方案是一个糟糕的解决方案。

  • 第三个解决方案 (start/stop) 将不以计时器设置的速率处理事件,而是以取决于每个特定操作所花费的时间的速率来处理事件。所以它可能 "skip" 许多事件。这个解决方案就像视频流中的"frame drop"。

  • 第二种解决方案会将所有操作排入队列并且不会跳过它们。当操作的处理时间(几乎总是)比计时器间隔长时,这有潜在的危险。队列只会在某个时间增长导致 OutOfMemoryException。这个解决方案就像视频流中的"frame buffer"。

  • 应该删除第一个,只有那个有问题。

因此,您应该根据对您的用例的重要性在第二个和第三个解决方案之间进行选择:对所有传入事件的可靠处理或具有最大可能吞吐量(速率)的处理。