我是新来的......
我有一个问题,如果有人可以帮助我。
关于计时器(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参数,但似乎没有任何帮助。
有什么想法吗?
答案 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
将执行第一个方法,但仅当_isSettingAOrB
为false
时才执行第二个方法。但是,它会在拨打电话之前将_isSettingAOrB
设置为true
。这将阻止无限递归的发生。最后一部分是在try-finally块中完成的,以保证如果被调用的方法抛出异常,该标志也会被重置。