C# 非重入定时器
我有一个函数,我想每x秒调用一次,但我希望它是线程安全的 我可以在创建计时器时设置此行为吗?(我不介意使用哪个.NET计时器,我只希望它是线程安全的) 我知道我可以在回调函数中实现锁,但我认为如果是在计时器级别,它会更优雅 我的回调函数和环境与UI无关 [编辑1] 我只是不希望回调函数中有多个线程 [编辑2] 我希望将锁定保持在计时器级别内,因为计时器负责何时调用回调函数,这里有一种特殊情况,即我不想调用回调函数。所以我认为什么时候打电话是计时器的责任 我知道我可以在回调函数中实现锁,但我认为如果是在计时器级别,它会更优雅 如果需要锁定,那么定时器如何安排呢?你在找一个神奇的免费赠品 重新编辑1:C# 非重入定时器,c#,timer,thread-safety,C#,Timer,Thread Safety,我有一个函数,我想每x秒调用一次,但我希望它是线程安全的 我可以在创建计时器时设置此行为吗?(我不介意使用哪个.NET计时器,我只希望它是线程安全的) 我知道我可以在回调函数中实现锁,但我认为如果是在计时器级别,它会更优雅 我的回调函数和环境与UI无关 [编辑1] 我只是不希望回调函数中有多个线程 [编辑2] 我希望将锁定保持在计时器级别内,因为计时器负责何时调用回调函数,这里有一种特殊情况,即我不想调用回调函数。所以我认为什么时候打电话是计时器的责任 我知道我可以在回调函数中实现锁,但我认为如
您的选择是System.Timers.Timer和System.Threading.Timer,两者都需要防止重新进入。请参阅并查找处理计时器事件重入的部分 计时器如何知道您的共享数据 计时器回调在某些线程池线程上执行。因此,您将至少有2个线程:
重新编辑:chibacity提供了一个完美的例子。我猜,由于您的问题并不完全清楚,您希望确保计时器在处理回调时不能重新进入回调,并且您希望在不锁定的情况下执行此操作。您可以使用
System.Timers.Timer
并确保AutoReset
属性设置为false来实现这一点。这将确保您必须在每个间隔手动触发计时器,从而防止任何重新进入:
public class NoLockTimer : IDisposable
{
private readonly Timer _timer;
public NoLockTimer()
{
_timer = new Timer { AutoReset = false, Interval = 1000 };
_timer.Elapsed += delegate
{
//Do some stuff
_timer.Start(); // <- Manual restart.
};
_timer.Start();
}
public void Dispose()
{
if (_timer != null)
{
_timer.Dispose();
}
}
}
public类NoLockTimer:IDisposable
{
专用只读定时器_定时器;
公共NoLockTimer()
{
_计时器=新计时器{AutoReset=false,间隔=1000};
_timer.appead+=委托
{
//做点什么
_timer.Start();//补充了Tim Lloyd针对System.Timers.timer
的解决方案,这里有一个解决方案,可以防止在需要使用System.Threading.timer
的情况下重入
TimeSpan DISABLED_TIME_SPAN = TimeSpan.FromMilliseconds(-1);
TimeSpan interval = TimeSpan.FromSeconds(1);
Timer timer = null; // assign null so we can access it inside the lambda
timer = new Timer(callback: state =>
{
doSomeWork();
try
{
timer.Change(interval, DISABLED_TIME_SPAN);
}
catch (ObjectDisposedException timerHasBeenDisposed)
{
}
}, state: null, dueTime: interval, period: DISABLED_TIME_SPAN);
我相信您不希望在回调内部访问interval
,但是如果您想:将上述内容放入一个nonerenttimer
类中,该类包装BCL的Timer
类。然后您可以将doSomeWork
回调作为参数传递进来。此类示例:
public class NonReentrantTimer : IDisposable
{
private readonly TimerCallback _callback;
private readonly TimeSpan _period;
private readonly Timer _timer;
public NonReentrantTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
{
_callback = callback;
_period = period;
_timer = new Timer(Callback, state, dueTime, DISABLED_TIME_SPAN);
}
private void Callback(object state)
{
_callback(state);
try
{
_timer.Change(_period, DISABLED_TIME_SPAN);
}
catch (ObjectDisposedException timerHasBeenDisposed)
{
}
}
public void Dispose()
{
_timer.Dispose();
}
}
使用系统;
使用系统诊断;
///
///更新了代码。
///
公共类NicerFormTimer:IDisposable{
公共空间处置(){
使用(this.Timer){}
总干事(本);
}
private System.Windows.Forms.Timer{get;}
///
///在给定的间隔(in)后执行。
///
///
///再次执行。(重新启动。)
///
public NicerFormTimer(动作动作,布尔重复,Int32?毫秒=null){
if(action==null){
返回;
}
this.Timer=new System.Windows.Forms.Timer{
间隔=毫秒。GetValuerDefault(1000)
};
this.Timer.Tick+=(发送方,参数)=>{
试一试{
this.Timer.Stop();
动作();
}
捕获(异常){
Debug.WriteLine(异常);
}
最后{
如果(重复){
this.Timer.Start();
}
}
};
this.Timer.Start();
}
}
///
///更新了代码。
///
公共类尼斯系统计时器:IDisposable{
公共空间处置(){
使用(this.Timer){}
总干事(本);
}
private System.Timers.Timer{get;}
///
///在给定的间隔(in)后执行。
///
///
///再次执行。(重新启动。)
///
public NicerSystemTimer(动作动作,布尔重复,双毫秒=null){
if(action==null){
返回;
}
this.Timer=new System.Timers.Timer{
自动重置=错误,
间隔=毫秒。GetValuerDefault(1000)
};
this.Timer.appeased+=(发送方,参数)=>{
试一试{
this.Timer.Stop();
动作();
}
捕获(异常){
Debug.WriteLine(异常);
}
最后{
如果(重复){
this.Timer.Start();
}
}
};
this.Timer.Start();
}
}
您没有提供足够的信息…您的函数是否访问程序的任何其他部分?如果没有,则它已经是线程安全的(有计时器和没有计时器)…如果是,则计时器与threadsafety无关,但您需要使用一些锁等。为什么要将锁完全排除在回调函数之外?编辑2没有意义:计时器负责按时调用您的函数,您的函数负责正确处理共享数据(lokcing等)-时间不能对你的数据负责,你的功能是…计时器本身是线程安全的-你的代码可能不是,但给出的信息不足以回答如何使其线程安全。请参阅我的编辑1,它对你有意义吗?@Hank I read
using System;
using System.Diagnostics;
/// <summary>
/// Updated the code.
/// </summary>
public class NicerFormTimer : IDisposable {
public void Dispose() {
using ( this.Timer ) { }
GC.SuppressFinalize( this );
}
private System.Windows.Forms.Timer Timer { get; }
/// <summary>
/// Perform an <paramref name="action" /> after the given interval (in <paramref name="milliseconds" />).
/// </summary>
/// <param name="action"></param>
/// <param name="repeat">Perform the <paramref name="action" /> again. (Restarts the <see cref="Timer" />.)</param>
/// <param name="milliseconds"></param>
public NicerFormTimer( Action action, Boolean repeat, Int32? milliseconds = null ) {
if ( action == null ) {
return;
}
this.Timer = new System.Windows.Forms.Timer {
Interval = milliseconds.GetValueOrDefault( 1000 )
};
this.Timer.Tick += ( sender, args ) => {
try {
this.Timer.Stop();
action();
}
catch ( Exception exception ) {
Debug.WriteLine( exception );
}
finally {
if ( repeat ) {
this.Timer.Start();
}
}
};
this.Timer.Start();
}
}
/// <summary>
/// Updated the code.
/// </summary>
public class NicerSystemTimer : IDisposable {
public void Dispose() {
using ( this.Timer ) { }
GC.SuppressFinalize( this );
}
private System.Timers.Timer Timer { get; }
/// <summary>
/// Perform an <paramref name="action" /> after the given interval (in <paramref name="milliseconds" />).
/// </summary>
/// <param name="action"></param>
/// <param name="repeat">Perform the <paramref name="action" /> again. (Restarts the <see cref="Timer" />.)</param>
/// <param name="milliseconds"></param>
public NicerSystemTimer( Action action, Boolean repeat, Double? milliseconds = null ) {
if ( action == null ) {
return;
}
this.Timer = new System.Timers.Timer {
AutoReset = false,
Interval = milliseconds.GetValueOrDefault( 1000 )
};
this.Timer.Elapsed += ( sender, args ) => {
try {
this.Timer.Stop();
action();
}
catch ( Exception exception ) {
Debug.WriteLine( exception );
}
finally {
if ( repeat ) {
this.Timer.Start();
}
}
};
this.Timer.Start();
}
}