2010-03-15 116 views
1

我是新来的...System.Threading.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线将立即触发上面的方法,但会有没有递归,因为标志是错误的。 在10 ms标志应该回到真,并且一切恢复正常。

现在,当我按照DEBUGGER中的代码,一切工作正常,但是当我启动应用程序NORMALLY复位功能根本不会触发,标志永远被卡住为假,一切都假分开。我玩due_time参数,但似乎没有任何帮助。

任何想法?

回答

0

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

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

2

听起来像你有各种讨厌的比赛在这里进行。你真的需要解决你的底层问题

2

看来你真正的问题是StackOverflow异常,由于非终止递归 - 你应该修复,然后不需要使用这样的定时器。

+0

没有,StackOverflow上是不可避免的。无论用户选择什么,用户都可以输入含增值税的价格或不含增值税的价格我的工作是设置另一个,当然,打破递归。 – 2010-03-15 19:09:52

+0

包装用户在属性中设置的列,当设置该属性时,自动设置该属性中的另一个属性。听起来不像这需要定时器。 – 2010-03-15 19:29:47

+1

@mijatovic - 我怀疑这是不可避免的 - 但是你打算用同步的计时器来防止它。 – Lee 2010-03-15 19:30:10

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块中完成的,以保证如果被调用的方法抛出异常,该标志也会被重置。

+0

这听起来不错...但是,你是通过这种方式参数传递函数?但是,我仍然不知道这是否会有所帮助。我的更改发生在我订阅的datarow的datacolumnchanged事件中,这意味着每当我更改一个值时,同一个事件将再次触发,如果我不打破它,则永远触发。我可以在更改之前取消订阅,并订阅后,但这意味着其他值不会,也就是说,“更新”。 你认为我可以用你的代码完成我所需要的吗?说实话,我需要一些时间来充分理解你的代码。 – 2010-03-15 19:27:59

+0

@mijatovic:我无法保证DataColumnChanged事件的行为如何(没有使用它),但通常事件处理程序是同步调用的(它们在同一个线程上执行),所以我认为它可以很好地工作。至于代码如何工作,我会用一些解释来更新答案。 – 2010-03-15 19:37:02

+0

我现在看到。好东西。虽然,为了解决我的问题,我必须在更改任何值之前取消订阅ColumnChanged事件,使其免于更改,然后再次订阅。这就像使这两个变化成为原子。谢谢。很有帮助。 – 2010-03-15 20:10:06

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; 
} 
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 
} 
+0

你说得对。我将计时器声明为表单的成员,所以它永远不会超出范围,现在一切正常。然而使用'使用'虽然没有帮助。感谢解决方案。 – 2010-03-15 19:57:10

+1

不幸的是,OP并没有将此标记为答案,而是。 – 2011-12-20 15:19:35

+0

我同意这样做,但我仍然不清楚为什么。如果我使用发布模式进行编译,然后使用反射器查看程序集,定时器仍然存在,那么它在哪里得到优化? – 2012-09-17 09:19:37