2

我有一个计时器线程功能

SampleTimer = new Timer(SomeTask,0,70000)

回调函数如下

void SomeTask(object o)
{
//block using autoresetevent
}

问题是 SomeTask() 回调方法每 70 秒调用一次,即使回调方法中的所有操作仍未完成。如何防止计时器在其中的所有步骤完成之前调用 SomeTask() 函数

4

3 回答 3

8

我假设您使用的是System.Threading.Timer.

一种方法是一次性创建计时器,然后在线程完成其任务后重新启动它。这样你就确定你不会有任何重叠:

myTimer = new Timer(someMethod, null, 70000, Timeout.Infinite);

在你的回调中:

void TimerCallback(object o)
{
    // do stuff here
    // then change the timer
    myTimer.Change(70000, Timeout.Infinite);
}

指定Timeout.Infinite周期会禁用周期性信令,将定时器变成一次性的。

另一种方法是使用监视器:

object TimerLock = new object();

void TimerCallback(object o)
{
    if (!Monitor.TryEnter(TimerLock))
    {
        // already in timer. Exit.
        return;
    }

    // do stuff

    // then release the lock
    Monitor.Exit(TimerLock);
}

如果您想知道为什么我不使用 atry/finally作为锁,请参阅 Eric Lippert 的博客Locks and exceptions do not mix

这两种方法的主要区别在于,在第一种方法中,计时器将在前一个回调执行完成后 70 秒触发。在一秒钟内,计时器将在 70 秒的周期内触发,因此下一个回调可能会在前一个回调完成后的任何时间执行,从一秒后到 70 秒后。

对于我所做的大多数事情,我展示的第一个技术似乎效果更好。

于 2013-08-01T14:47:09.733 回答
0

并发调用是可能的,这是 .NET 和 Windows 计时器的一个非常烦人的属性。您必须自己确保互斥,也许只是使用锁(不需要事件)。但是,这给您带来了另一个问题,因为如果计时器计时太快,则任意数量的线程可能会在锁前排队。所以最好TryEnter锁好,进不去就什么都不做。您以这种方式丢失了一个刻度,但由于无论如何都不能保证刻度,您的应用程序必须能够应对这种情况。

使用计时器时,几乎无法保证滴答的时间、计数和并发性。

于 2013-08-01T14:03:47.537 回答
0

我相信这object o 被解雇的计时器。所以你可以这样做:

var myTimer = object as Timer;

(但我会检查一下......)

无论您SomeTask需要多长时间,它都会在您的时间间隔内触发。

我总是做这样的事情

private System.Timers.Timer _timer;

public static void main()
{
    _timer = SampleTimer = new Timer(SomeTask,0,70000);
}

void SomeTask(object o)
{
    _timer.Enabled = false;
    try{
    // ...  work...
    }finally{
        _timer.Enabled = true;
    }
}

更新:带锁更安全。

private static object _padLock = new object();

void SomeTask(object o)
{
    // lock around disabling the timer
    lock(_padLock)
    {
        // return if some other thread beat us to it.
        if (!_timer.Enabled) return;
        _timer.Enabled = false;
    }

    try{
    // ...  work...
    }finally{
        _timer.Enabled = true;
    }
}

为了超级安全。如果您只想运行一个“SomeTask”实例。而且,您不希望他们排队。您应该在 SomeTask 中放置一个 lock()。您可以跳过禁用计时器。

于 2013-08-01T14:07:20.220 回答