C# Threading.Timer 为啥过了一会儿自己停了
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# Threading.Timer 为啥过了一会儿自己停了相关的知识,希望对你有一定的参考价值。
最近咱一研究Threading.Timer,因为之前的Forms.Timer 总是会出现卡屏的现象,所以觉得改用Threading.timer,可是我从网上找了一下方法,但是出现了一些问题,想向大家请教,我的源代码如下:
private void button1_Click(object sender, EventArgs e)
System.Threading.Timer timer1 = new System.Threading.Timer(new TimerCallback(timer_Elapsed), null, 0, 1000);
Thread t; //定义线程变量
private delegate void setText();//定义一个线程委托
//自定义方法Threadp,主要用于线程的调用
public void Threadp()
setText d = new setText(Threading); //实例化一个委托
this.Invoke(d); //在拥用此控件的基础窗体句柄的线程上执行指定的委托
//自定义方法Threading,主要作于委托的调用
public void Threading()
textBox1.Text = DateTime.Now.ToString();
t.Abort();//关闭线程
void timer_Elapsed(object sender)
t = new Thread(new ThreadStart(Threadp)); //实例化线程
t.Start();//启动线程
程序的功能即在TextBox中显示当前时间,一秒一刷新,但是呢,我测试了一些,发现Timer启动后,64s后就自己停止了,不知道为什么,各位大侠给瞧瞧。
可能原因:自定义方法Threading()是在UI主线程空闲上执行,而timer_Elapsed是在线程池当中的一个线程上执行,两者执行时很可能在执行 void timer_Elapsed(object sender)
t = new Thread(new ThreadStart(Threadp)); //实例化线程
//线程池当中的线程执行到这里时,UI主线程执行Threading()的 t.Abort();//关闭线程
t.Start();//启动线程
解决:你将所有方法都用try括起来,捕获异常信息,你一看就明白了。
建议:你一秒刷新一次TextBox控件,为什么要建一个Thread呢?(这个是很消耗资源的),可以像下面这样写。同时还可以查看this.Invoke和this.BeginInvoke的区别。这个还可以使用BackgroundWorker来完成这个功能。
private void button1_Click(object sender, EventArgs e)
System.Threading.Timer timer1 = new System.Threading.Timer(new TimerCallback(timer_Elapsed), null, 0, 1000);
//将这个d定义字段,放到方法外部
setText d = new setText(Threading); //实例化一个委托
void timer_Elapsed(object sender)
this.Invoke(d); //在拥用此控件的基础窗体句柄的线程上执行指定的委托
追问
这只是一个测试软件,实际上我是要在Timer中执行数据库操作及IO操作,很多东西,所以实际当中,开启一个线程还会消耗资源吗?我的目的就是为了解决,Forms.Timer卡的问题,现在Threading.Timer,我发现出现执行60次左右就停止的现象,所以就有点不知道怎么回事了。我试试你的方法吧,感谢你。
参考技术A private void button1_Click(object sender, EventArgs e)System.Threading.Timer timer1 = new System.Threading.Timer(new TimerCallback(timer_Elapsed), null, 0, 1000);
GC.KeepAlive(timer1);
timer被回收了,加这个GC.KeepAlive(timer1);我以前也遇到过,这么解决的追问
非常感谢,我最后还是通过System.Threading.Timer timer1(全局),然后timer1 = new System.Threading.Timer(new TimerCallback(timer_Elapsed), null, 0, 1000);
这样就不会回收了。
C# 中的 System.Threading.Timer 似乎无法正常工作。它每 3 秒运行一次非常快
【中文标题】C# 中的 System.Threading.Timer 似乎无法正常工作。它每 3 秒运行一次非常快【英文标题】:System.Threading.Timer in C# it seems to be not working. It runs very fast every 3 second 【发布时间】:2012-09-29 13:19:23 【问题描述】:我有一个计时器对象。我希望它每分钟运行一次。具体来说,它应该运行OnCallBack
方法并在OnCallBack
方法运行时变为非活动状态。一旦OnCallBack
方法完成,它(OnCallBack
)就会重新启动计时器。
这是我现在拥有的:
private static Timer timer;
private static void Main()
timer = new Timer(_ => OnCallBack(), null, 0, 1000 * 10); //every 10 seconds
Console.ReadLine();
private static void OnCallBack()
timer.Change(Timeout.Infinite, Timeout.Infinite); //stops the timer
Thread.Sleep(3000); //doing some long operation
timer.Change(0, 1000 * 10); //restarts the timer
但是,它似乎不起作用。它每 3 秒运行一次非常快。即使如果提高一个周期(1000 * 10)。好像对1000 * 10
视而不见@
我做错了什么?
【问题讨论】:
来自Timer.Change
:“如果dueTime为零(0),则立即调用回调方法。”。对我来说似乎是零。
是的,但那又怎样?也有一个时期。
如果也有月经怎么办?引用的句子没有对期间价值提出任何要求。它只是说“如果这个值为零,我将立即调用回调”。
有趣的是,如果你将dueTime和period都设置为0,计时器将每秒运行一次并立即启动。
【参考方案1】:
这不是 System.Threading.Timer 的正确用法。当您实例化 Timer 时,您几乎总是应该执行以下操作:
_timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
这将指示计时器在间隔过去后仅计时一次。然后在您的回调函数中,您在工作完成后更改计时器,而不是之前。示例:
private void Callback( Object state )
// Long running operation
_timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
因此不需要锁定机制,因为没有并发性。计时器将在下一个间隔过去 + 长时间运行操作的时间后触发下一个回调。
如果你需要精确地运行你的计时器 N 毫秒,那么我建议你使用 Stopwatch 测量长时间运行操作的时间,然后适当地调用 Change 方法:
private void Callback( Object state )
Stopwatch watch = new Stopwatch();
watch.Start();
// Long running operation
_timer.Change( Math.Max( 0, TIME_INTERVAL_IN_MILLISECONDS - watch.ElapsedMilliseconds ), Timeout.Infinite );
我强烈鼓励任何正在使用 .NET 并且正在使用 CLR 的人但没有阅读过 Jeffrey Richter 的书 - CLR via C#,请尽快阅读。定时器和线程池在那里有非常详细的解释。
【讨论】:
我不同意private void Callback( Object state ) // Long running operation _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
。 Callback
可能会在操作完成之前再次调用。
我的意思是Long running operation
可能比TIME_INTERVAL_IN_MILLISECONDS
花费更多的时间。那会发生什么?
回调不会再被调用,这就是重点。这就是我们将 Timeout.Infinite 作为第二个参数传递的原因。这基本上意味着不要再为计时器打勾。然后在我们完成操作后重新安排打勾。
这里是线程的新手——如果你传入计时器,你认为这可能与ThreadPool
相关吗?我正在考虑这样一种情况,即在某个时间间隔产生一个新线程来执行一项工作 - 然后在完成时降级到线程池。
System.Threading.Timer 是一个线程池计时器,它在线程池上执行它的回调,而不是专用线程。定时器完成回调例程后,执行回调的线程回到池中。【参考方案2】:
定时器不需要停止,see nice solution from this post:
"您可以让计时器继续触发回调方法,但将不可重入代码包装在 Monitor.TryEnter/Exit 中。在这种情况下无需停止/重新启动计时器;重叠调用不会获取锁并返回马上。”
private void CreatorLoop(object state)
if (Monitor.TryEnter(lockObject))
try
// Work here
finally
Monitor.Exit(lockObject);
【讨论】:
这不适合我的情况。我需要准确地停止计时器。 您是否试图阻止多次输入回调?如果没有,你想达到什么目的? 1.防止多次进入回调。 2.防止执行次数过多。 这正是它的作用。 #2 只要在 if 语句 if 对象被锁定后立即返回,开销并不大,特别是如果你有这么大的间隔。 这并不能保证在最后一次执行后不少于是否必须使用System.Threading.Timer
?
如果没有,System.Timers.Timer
有方便的 Start()
和 Stop()
方法(还有一个 AutoReset
属性可以设置为 false,这样就不需要 Stop()
,您只需在执行后调用 Start()
)。
【讨论】:
是的,但这可能是一个真正的要求,或者只是碰巧选择了计时器,因为它是最常用的。遗憾的是,.NET 有大量的计时器对象,重叠了 90%,但仍然(有时微妙地)不同。当然,如果是需求的话,这个方案根本不适用。 根据documentation:Systems.Timer 类仅在 .NET Framework 中可用。它不包含在 .NET 标准库中,并且在其他平台上不可用,例如 .NET Core 或通用 Windows 平台。在这些平台上,以及跨所有 .NET 平台的可移植性,您应该改用 System.Threading.Timer 类。【参考方案4】:我会这样做:
private static Timer timer;
private static void Main()
timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
Console.ReadLine();
private static void OnCallBack()
timer.Dispose();
Thread.Sleep(3000); //doing some long operation
timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
并忽略 period 参数,因为您正在尝试自己控制周期。
您的原始代码正在尽可能快地运行,因为您一直为 dueTime
参数指定 0
。来自Timer.Change
:
如果dueTime为零(0),则立即调用回调方法。
【讨论】:
是否需要配置定时器?为什么不用Change()
方法?
每次都设置计时器是绝对没有必要和错误的。
@IvanZlatanov 您能否就此提供更多见解。当我们调用 dispose 时,对象将从内存中清除,这很好,对吧?我所看到的只是一些可能影响解决方案的额外过程。只是大声思考......
@SudhakarChavali 您应该始终在使用完对象后将其丢弃。在这个例子中,因为定时器有 Change 方法可以启用/禁用实例,所以不需要释放它并创建一个新的。【参考方案5】:
var span = TimeSpan.FromMinutes(2);
var t = Task.Factory.StartNew(async delegate / () =>
this.SomeAsync();
await Task.Delay(span, source.Token);
, source.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
source.Cancel(true/or not);
// or use ThreadPool(whit defaul options thread) like this
Task.Start(()=>...), source.Token)
如果你喜欢在里面使用一些循环线程...
public async void RunForestRun(CancellationToken token)
var t = await Task.Factory.StartNew(async delegate
while (true)
await Task.Delay(TimeSpan.FromSeconds(1), token)
.ContinueWith(task => Console.WriteLine("End delay"); );
this.PrintConsole(1);
, token) // drop thread options to default values;
// And somewhere there
source.Cancel();
//or
token.ThrowIfCancellationRequested(); // try/ catch block requred.
【讨论】:
以上是关于C# Threading.Timer 为啥过了一会儿自己停了的主要内容,如果未能解决你的问题,请参考以下文章
为啥 System.Timers.Timer 能在 GC 中存活,而 System.Threading.Timer 不能?
System.Threading.Timer:它为啥讨厌我?
如何在 C# 中创建计时器而不使用 System.Timers.Timer 或 System.Threading.Timer