我正在处理一个问题。事实上,我需要在我的程序中使用不同的计时器。来自Framework .Net的计时器不能做我期望的事情。所以我决定创建自己的Timer,但我的Timer使用了太多的CPU。这是我的代码:
using System;
using System.Threading;
namespace XXXXXXX.Common.Types
{
public delegate void TimerFinishedEventHandler(object sender, EventArgs e);
class Timer
{
#region Attributes
private long m_InitialTickCount;
private long m_Interval;
private Thread m_Thread;
private bool m_Enabled;
#endregion
#region Events
public event TimerFinishedEventHandler Finished;
#endregion
#region Constructors
public Timer(long interval, TimerFinishedEventHandler e)
{
Finished += e;
m_Interval = interval;
Start(m_Interval);
}
#endregion
#region Public methods
/// <summary>
/// Start the timer thread.
/// </summary>
public void Start(long interval)
{
m_Interval = interval;
m_Enabled = true;
m_InitialTickCount = Environment.TickCount;
if (m_Thread == null)
{
m_Thread = new Thread(Check);
m_Thread.Start();
}
}
/// <summary>
/// Stop the Timer.
/// </summary>
public void Stop()
{
m_Enabled = false;
}
/// <summary>
/// Restart the Timer.
/// </summary>
public void Restart()
{
m_InitialTickCount = Environment.TickCount;
}
#endregion
#region Private methods
/// <summary>
/// Check if the timer is finished or not.
/// </summary>
private void Check()
{
while (true)
{
if (!m_Enabled)
return;
if (Environment.TickCount > m_InitialTickCount + m_Interval)
{
OnFinished(EventArgs.Empty);
return;
}
}
}
/// <summary>
/// Called when the Timer is Finished.
/// </summary>
/// <param name="e">Event</param>
protected virtual void OnFinished(EventArgs e)
{
if (Finished != null)
Finished(this, e);
}
#endregion
}
}
有没有人有解决方案?因为当我启动我的程序时,会创建2个或3个计时器,另一个线程运行,我100%使用我的CPU。
答案 0 :(得分:2)
绝对没有理由你没有多个计时器。我的程序有数百的计时器,并且在任何时候它们中的一些都可以实际工作。定时器的重点是它们允许您安排定期操作,而不消耗任何CPU资源,除非实际处理操作。也就是说,如果你将一个计时器设置为每分钟一次,那么该计时器不会占用一个线程,不会占用任何内存(超出计时器句柄和回调地址的令牌数量),以及不消耗任何CPU资源。只有当计时器&#34;滴答&#34;每分钟一次是分配给它执行代码的线程。通常这是一个已经存在的池线程,因此线程启动时间可以忽略不计。
使用计时器非常简单:您可以创建一个执行计时器的方法,并安排计时器运行它。例如:
System.Threading.Timer myTimer =
new System.Threading.Timer(MyTimerProc, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
void MyTimerProc(object state)
{
// do something here
}
您可以使用另一个计时器,每30秒计时一次并执行不同的计时器过程:
System.Threading.Timer myOtherTimer =
new System.Threading.Timer(MyOtherTimerProc, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
void MyOtherTimerProc(object state)
{
// do something else here
}
计时器不会互相干扰。当然,如果timer procs中的代码修改了共享数据(例如,两个proc都尝试更新列表或字典),那么您必须同步对该共享数据的访问或使用并发数据结构。 / p>
如果计时器过程中的处理时间超过计时器周期,那么可能在重入时遇到麻烦。如果MyTimerProc
中的处理时间超过60秒,则可能会出现另一个计时器滴答,现在您有两个线程正在执行该计时器proc。如果您的代码未设置为处理它,则可能会导致许多不同类型的问题。通常,您可以通过使计时器成为一次性并在每个处理周期结束时重新启动它来消除该问题。在Stack Overflow上有这样做的例子。
System.Timers.Timer
是System.Threading.Timer
周围的组件包装器。它为高性能线程优化的想法&#34;或者其中一些是愚蠢的。 System.Timers.Timer
为您提供了一个熟悉的面向事件的界面,并提供了SynchronizingObject
,它允许您在特定线程上引发事件,而不必像{Invoke
那样明确System.Threading.Timer
{1}}。通常,这仅适用于UI应用程序。
System.Timers.Timer
有一个特别丑陋的功能&#34;我认为是一个错误:它会挤压异常。正如the documentation所说:
在.NET Framework 2.0及更早版本中,Timer组件捕获并抑制事件处理程序为Elapsed事件抛出的所有异常。
.NET 4.5中仍然存在这种行为。问题是如果你有Elapsed
事件:
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
// do stuff here
}
如果您的事件处理程序抛出异常,它将传播回定时器代码,该代码会压缩异常并且永远不会告诉您。实际上,计时器执行此操作:
try
{
OnTimedEvent(source, args);
}
catch
{
// Squash those pesky exceptions. Who needs them?
}
这是一个隐藏的错误,因为你永远不知道抛出异常。所以你的程序不起作用,你无法弄清楚原因。因此我强烈建议您不要使用System.Timers.Timer
。请改用System.Threading.Timer
;毕竟,它是构建System.Timers.Timer
的基础。
答案 1 :(得分:1)
要直接回答您的问题,您的CPU使用率如此之高的原因是您使用紧密的while循环来检查已过去的事件。这有时被称为自旋锁(因为它是一种非常低效的实现信号量的方式,其中线程在紧密循环中检查锁定变量,因此它“旋转”)。 / p>
而不是紧密循环,你需要阻止并允许其他东西运行一段时间:
private void Check()
{
while (true)
{
if (!m_Enabled)
return;
Thread.Sleep(10); //10 millisecond resolution for this timer
if (Environment.TickCount > m_InitialTickCount + m_Interval)
{
OnFinished(EventArgs.Empty);
return;
}
}
}
分辨率取决于你的睡眠时间。话虽这么说,提供的计时器应该总是足够的,即使使用2 System.Threading.Timers也可以工作。我亲自使用了System.Threading.Timer和System.Timers.Timer的多个版本,没有任何问题。
当然,对于所有这些计时器,您需要注意访问共享资源(可能是您阻止其他线程的现有计时器的意思?)。死锁是多线程中非常真实的场景,但与定时器没什么关系。
答案 2 :(得分:0)
@Jim Mischel。
在我的班级连接到我的网站,检查数据:
#region Attributes
private static Timer m_TimerNextCheck;
#endregion
#region Méthodes publiques
public static void StartCheck()
{
Thread licenceThread = new Thread(Checking);
licenceThread.Start();
}
#endregion
#region Méthodes privées
private static void Checking()
{
//connect to the website
try
{
HttpWebResponse httpWebResponse = (HttpWebResponse) request.GetResponse();
StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream(), Encoding.Default);
string response = streamReader.ReadToEnd();
httpWebResponse.Close();
if (//Some code)
{
//Some code
}
else
{
if (m_TimerNextCheck == null)
m_TimerNextCheck = new Timer(TimerNextCheckFinished, null, 300000, Timeout.Infinite);
else
m_TimerNextCheck.Change(300000, Timeout.Infinite);
}
}
catch (WebException exception)
{
//Some code
if (m_TimerNextCheck == null)
m_TimerNextCheck = new Timer(TimerNextCheckFinished, null, 60000, Timeout.Infinite);
else
m_TimerNextCheck.Change(60000, Timeout.Infinite);
}
}
private static void TimerNextCheckFinished(object statusInfos)
{
Checking();
}
#endregion
在另一堂课:
#region Attributs
private Thread m_ConnectionThread;
private Timer m_TimerConnectionThread;
#endregion
#region Méthodes publiques
public void Launch()
{
m_ConnectionThread = new Thread(Connect);
m_ConnectionThread.Start();
}
public void GetNextMeal()
{
//Some code
if (//Some code)
{
//Some code
if (m_TimerConnectionThread == null)
m_TimerConnectionThread = new Timer(TimerConnectionThreadFinished, null,
(int)TimeSpan.FromHours(difference.Hour).TotalMilliseconds +
(int)TimeSpan.FromMinutes(difference.Minute).TotalMilliseconds, Timeout.Infinite);
else
m_TimerConnectionThread.Change((int)TimeSpan.FromHours(difference.Hour).TotalMilliseconds +
(int)TimeSpan.FromMinutes(difference.Minute).TotalMilliseconds, Timeout.Infinite);
}
else
{
//Some code
}
}
public void TryReconnect(int minute)
{
//Some code
if (m_TimerConnectionThread == null)
m_TimerConnectionThread = new Timer(TimerConnectionThreadFinished, null, (int)TimeSpan.FromMinutes(minute).TotalMilliseconds,
Timeout.Infinite);
else
m_TimerConnectionThread.Change((int)TimeSpan.FromMinutes(minute).TotalMilliseconds, Timeout.Infinite);
//Some code
}
//Some code
#endregion
#region Méthodes privées
private void Connect()
{
if (m_TimerConnectionThread != null)
m_TimerConnectionThread.Change(Timeout.Infinite, Timeout.Infinite);
//Some code
}
//Some code
private void TimerConnectionThreadFinished(object stateInfo)
{
Connect();
}
#endregion
它运作良好!