哪个是对 System.Timers.Timer 使用 Stop/Start timer vs SynchronizingObject vs lock?
Posted
技术标签:
【中文标题】哪个是对 System.Timers.Timer 使用 Stop/Start timer vs SynchronizingObject vs lock?【英文标题】:Which is to use Stop/Start timer vs SynchronizingObject vs lock for System.Timers.Timer? 【发布时间】:2018-04-20 04:27:47 【问题描述】:重要提示:我猜每个 cmets 都是可行的解决方案 with this note 实际上,在我的应用程序中,我正在尝试将它们结合起来。顺便说一句,在投反对票之前,您可以向我询问详细信息。
重要提示 2:顺便说一下,由于 SO 不同于 MSO 否决票或接近投票鼓励删除问题,我不会,否则所有有价值的 cmets 和答案都将被删除。这是一个互相帮助并尝试相互理解的地方
这是 linqpad 代码的 4 种不同实现中最基本的。除了首先所有其他人提供所需的输出。
你能为他们解释一下细节吗?
由于我的应用程序中有许多计时器,我需要以最适合使用的完整代码以及替代解决方案的优缺点来管理和同步
既没有 SynchronizingObject 也没有计时器停止/启动或锁定
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
带定时器启动/停止
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);
【问题讨论】:
这有什么用(以及实际问题是什么)。您似乎在征求一些与 SO 无关的意见。 我们无法为您选择可行的解决方案,对您的应用程序的需求一无所知。如果您有性能要求,无论是时间、内存还是争用,配置文件。否则,请选择您理解的最简单的解决方案,并拥有一个界面,让您在预见需要更改时轻松切换方法。 @ErikPhilips 我不明白他们之间的区别,我问lock
ing 在这种情况下是危险的,它可能会炸毁你的线程池。其他两种解决方案都可以使用,具体选择取决于特定的用例。
@dymanoid 感谢您的有益和建设性评论
【参考方案1】:
第 4 种解决方案 (lock
) 很危险。由于Elapsed
事件每次都会在ThreadPool
线程上引发,并且您可能会同时阻止其中许多,这可能导致ThreadPool
增长(以及所有后果)。所以这个解决方案很糟糕。
第三种解决方案(开始/停止)将不以计时器设置的速率处理事件,而是以取决于每个特定操作花费多少时间的速率处理事件。所以它可能会“跳过”许多事件。这种解决方案就像视频流中的“丢帧”。
第二种解决方案会将所有操作排入队列并且不会跳过它们。当操作的处理时间(几乎总是)长于计时器间隔时,这是潜在的危险。队列只会在某个时候增长导致OutOfMemoryException
。这种解决方案就像视频流中的“帧缓冲区”。
第一个应该删除,只有那个问题。
因此,您应该根据对您的用例的重要性在第 2 和第 3 解决方案之间进行选择:对所有传入事件进行可靠处理或以最大可能的吞吐量(速率)进行处理。
【讨论】:
它们都以同样的方式危险地错误,线程池队列总是会不堪重负。锁只是让它在 OOM 上更快地崩溃,比如两周而不是四个星期。让它变得更好:) 两种方式都很难调试。 @HansPassant,第三个有什么问题?我只看到没有AutoReset = false
。
好吧,没那么糟糕,但这是一场比赛。如果其他代码也在使用线程池,那么 Elapsed 将被延迟。而且计时器不会很快停止。以上是关于哪个是对 System.Timers.Timer 使用 Stop/Start timer vs SynchronizingObject vs lock?的主要内容,如果未能解决你的问题,请参考以下文章
使用System.Timers.Timer类实现程序定时执行
System.Windows.Forms.TimerSystem.Timers.TimerSystem.Threading.Timer