System.Threading.Timer不会触发

时间:2010-03-15 18:50:36

标签: c# timer

我是新来的......

我有一个问题,如果有人可以帮助我。

关于计时器(System.Threading.Timer)。

我想打破不可避免的递归:我在datarow中有两列,它们是相互依赖的(price_without_VAT和price_with_VAT)。设置其中一个肯定会导致StackOverflowException。所以这就是这个想法:

bool flag = true;
void Reset(object state) { flag = true; }

现在,包装用于更改其中一列的值的方法:

{
    if(flag)
    {
        flag = false;
        System.Threading.Timer tmr = new System.Threading.Timer(new System.Threading.TimerCallback(Reset), null, 10, System.Threading.Timeout.Infinite);
        datarow.other_column = value;
    }
}

datarow.other_column.value行将立即触发上述方法,但不会有递归,因为flag为false。 在10毫秒标志应该回到真,一切都恢复正常。

现在,当我按照DEBUGGER中的代码进行操作时,一切正常,但是当我启动app NORMALLY时,复位功能根本不会触发,标志会永远停留在虚假状态,并且一切都是假的。我玩弄due_time参数,但似乎没有任何帮助。

有什么想法吗?

6 个答案:

答案 0 :(得分:6)

虽然我同意那些说你应该找到另一种方法来阻止无限递归的人,但你的计时器不会触发的原因可能是因为它被优化了。我最近遇到了其他事情。

假设你想要一个周期性的计时器:

void SomeMethod()
{
    Timer MyTimer = new Timer(MyTimerProc, null, 3000, 3000);
    // other stuff goes here
}

现在,您在调试模式下运行它,一切正常。但是当你在发布模式下运行它时,计时器永远不会触发。这是因为它被优化了。您需要做的是保持活着(使用GC.KeepAlive)或using

using (Timer MyTimer = new Timer(MyTimerProc, null, 3000, 3000))
{
    // other stuff here
}

答案 1 :(得分:2)

听起来你在这里发生了各种令人讨厌的比赛。你真的需要解决你的潜在问题

答案 2 :(得分:2)

看起来你遇到的真正问题是由于非终止递归导致的StackOverflow异常 - 你应该修复它,然后就不需要像这样使用Timers了。

答案 3 :(得分:2)

使用lock而不是标志来确保一次只在一个线程中进行更新。

// class member
private object syncObject = new object();

// then, in your code...
lock(syncObject) {
   System.Threading.Timer tmr = new System.Threading.Timer(new System.Threading.TimerCallback(Reset), null, 10, System.Threading.Timeout.Infinite);
   datarow.other_column = value;
}

答案 4 :(得分:0)

我不会这样做。你会遇到计时问题。如果您尝试快速连续设置两个“已标记”值,会发生什么?这最多会导致奇怪的调试,最坏的情况是导致无效数据被保存。

您可以将标志设置为全局,将其设置在一个属性中,然后在另一个属性中进行检查。设置标志,更改值,然后在完成后设置标志。或者,让getter执行基础计算,并且不允许显式设置其中一个值。

答案 5 :(得分:0)

我不会在这里使用计时器。当我遇到这种问题时(通常不常见),我通常会做的事情就是这样:

bool _isSettingAOrB;
private int _a;
private void SetA(int value)
{
    _a = value;

    if (_isSettingAOrB)
    {
        return;
    }

    _isSettingAOrB = true;

    try
    {
        SetB(_a - 10);
    }
    finally
    {
        _isSettingAOrB = false;
    }
}

private int _b;
private void SetB(int value)
{
    _b = value;

    if (_isSettingAOrB)
    {
        return;
    }

    _isSettingAOrB = true;

    try
    {

        SetA(_b + 10);
    }
    finally
    {
        _isSettingAOrB = false;
    }
}

如果你不喜欢重复模式(如上面的代码所示),你可以将调用结构包装成一个单独的方法:

bool _isSettingAOrB;
private int _a;
public void SetA(int value)
{
    SetInterdependentValues(() => _a = value, () => SetB(_a - 10));
}

private int _b;
public void SetB(int value)
{
    SetInterdependentValues(() => _b = value, () => SetA(_b + 10));
}

private void SetInterdependentValues(Action primary, Action secondary)
{
    primary();

    if (_isSettingAOrB)
    {
        return;
    }
    _isSettingAOrB = true;
    try
    {
        secondary();
    }
    finally
    {
        _isSettingAOrB = false;
    }
}

代码的简要说明:

SetInterdependentValues(() => _a = value, () => SetB(_a - 10));

这是一个带有两个参数的方法调用。这两个参数是() => _a = value() => SetB(_a - 10)。简而言之,这些是lambda表达式,将转换为委托,其中每个方法的主体位于=>的右侧。所以第一个参数是一个将value赋给_a的方法,第二个参数将调用方法SetB,传递参数_a - 10

SetInterdependentValues将执行第一个方法,但仅当_isSettingAOrBfalse时才执行第二个方法。但是,它会在拨打电话之前将_isSettingAOrB设置为true。这将阻止无限递归的发生。最后一部分是在try-finally块中完成的,以保证如果被调用的方法抛出异常,该标志也会被重置。