【问题标题】:Error message "Cannot evaluate expression because the current thread is in a stack overflow state"错误消息“无法评估表达式,因为当前线程处于堆栈溢出状态”
【发布时间】:2017-02-13 21:14:16
【问题描述】:

我正在尝试创建对方法的递归调用但收到错误:

无法计算表达式,因为当前线程在堆栈中 溢出状态

只是一点背景知识,我正在迁移一个 PowerBuilder 应用程序。到 C#.NET windows 应用程序,PB 应用程序。有一个以编程方式完成的计时器,它将在特定时间执行 main 方法。 PB 代码在两台机器上运行,一个计时器设置在 '0' 标记处,而在 #2 机器上,它设置在 30 秒标记处。

PB 代码如下:

ue_wait event:

gi_offset = //Global variable set at the application open event, equivalent to program.cs - main event. Will be 0 or 30 depending on the machine.

li_difference = Integer( String( Now(), 'ss')) //Gets the seconds from the current date time.

DO UNTIL li_difference = gi_offset

 ls_status = 'current second: ' + String( Now(), 'ss') + ' starting on:' + String( gi_offset)// Builds a string to display in a static text control in PB, label in C#.

 IF st_status.Text <> ls_status THEN st_status.Text = ls_status

 Yield()

 li_difference = Integer( String( Now(), 'ss'))

LOOP

//execute main processing method.

ue_action().

//End of event

ue_action event
Do main processing.

Call ue_wait()

下面是我的 C# 代码:

private void Form1_Shown(Object sender, EventArgs e)        
{            
    ue_wait();        
}

private void wf_actions_Load(object sender, EventArgs e)        
{            
      //Should fire after all of the form has loaded.                            
      Shown += Form1_Shown;        
}  

private void ue_wait()        
{            
    long todays_date_in_seconds = DateTime.Now.Second;
    long ll_global_offset = 0;            
    string ls_status = "";                         

    todays_date_in_seconds = DateTime.Now.Second;            

    while (!(todays_date_in_seconds == ll_global_offset))   
    {                
        ls_status = "Current second: " + DateTime.Now.Second.ToString() + " starting on: " + ll_global_offset.ToString();                
        lbl_status.Text = ls_status;                
        todays_date_in_seconds = DateTime.Now.Second;            
    }             

    ue_action();  
}

private void ue_action()        
{            
//After the main processing is done, it goes back to ue_wait().            
    ue_wait();        
}

所以我尝试了在 Inet 上找到的两种可能性:

this.Shown += new System.EventHandler(this.Form1_Shown);

this.Activated += AfterLoading;

但是,当调用 ue_action 时,我在 ue_wait 事件中都收到以下错误...

{无法计算表达式,因为当前线程在堆栈中 溢出状态。}

第一行失败:long todays_date_in_seconds = DateTime.Now.Second;

我找到了 .NET 计时器,但它不允许您在特定时间点设置开始,即:秒、Timer.Start() = 0 标记或 Timer.Start() = 30 秒标记(30000 毫秒)。间隔是可以工作的一部分,因为我可以将其设置为每 60 秒执行一次。

做了一些谷歌搜索后,因为有“递归”编程,这导致了“堆栈溢出”,我怎样才能避免 .NET 环境中的“堆栈溢出”,或者是否有另一种方法来做我需要的在 C#.NET 环境中做什么?

【问题讨论】:

  • 我没有看到 ue_actionue_wait 方法有任何救助或提前回报。这两者怎么不会无限互相调用(从而导致堆栈溢出)?
  • ue_action 调用ue_wait 调用ue_action 调用ue_wait 调用ue_action 调用ue_wait 调用ue_action 调用ue_wait 调用@9876543调用ue_wait 调用ue_action 调用ue_wait 调用(你明白了)。无限循环是任何语言的无限循环,与 .NET 无关。
  • 我知道这是一个无限循环,这就是它在 PB 应用程序中的完成方式。该应用程序 24/7 运行并在自身内部不断循环以构建计时器并处理它需要在 ue_action 事件中处理的内容,然后继续返回 ue_wait 等...... DO UNTIL 循环中有 Yield() 函数Yield 控制其他图形对象,包括不是 PowerBuilder 对象的对象。Yield 检查消息队列,如果队列中有消息,它会将它们从队列中拉出。"。不确定此功能是否有助于堆栈溢出,可能在 .NET 中类似。
  • .NET 有一个 Application.DoEvents 方法,它的作用类似,但这是一种完全错误的方法。我怀疑您是否想将单个 CPU 内核旋转到 100% 只是为了每秒触发一个事件。在任何语言中使用循环进行计时都是一个坏主意,如果您可以使用简单的计时器,您甚至不会在 8051 微控制器中使用它。
  • 我同意你的看法,PB 应用程序是在 2004 年编写的,但开发工具没有像 .NET 这样的计时器控件。我自己是一名 PB 开发人员,我的大脑围绕着 .NET。感谢您的意见。

标签: c# .net winforms


【解决方案1】:

最简单的方法是使用 1s System.Windows.Forms.Timer,这很简单:

// timer initialization, somewhere
timer = new System.Windows.Forms.Timer();
timer.Interval = 1000;
timer.Tick += Timer_Tick;

DateTime? _lastHandledTimestamp = null;
private void Timer_Tick(object sender, EventArgs e)
{
    // get current timestamp
    var currentTimestamp = DateTime.UtcNow;
    var currentSecond = currentTimestamp.Second;

    // not the correct second yet?
    if (currentSecond <= TARGET_SECOND)
        return;

    // truncate seconds and check if we already handled this hh:mm
    var flooredToNearestMinute = currentTimestamp.AddSeconds(-currentSecond);
    if (_lastHandledTimestamp.HasValue &&
        _lastHandledTimestamp.Value >= flooredToNearestMinute)
        return;

    // if we're here, we are good to go
    _lastHandledTimestamp = flooredToNearestMinute;
    DoStuff();
}

如果您想避免每秒触发一次滴答事件,请改用System.Threading.Timer,并使用其Timer.Change 方法计算每次迭代需要触发的下一次时间。

【讨论】:

  • 我会试试的,非常感谢您的帮助。
【解决方案2】:

Groo,感谢您对计时器的输入,我根据需要对其进行了调整。

private void timer1_Tick(object sender, EventArgs e)
    {
        string ls_status = "";
        var currentSecond = DateTime.Now.Second;

        ls_status = "Current second: " + currentSecond.ToString() + " starting on: " + Il_offset.ToString();

        lbl_status.Text = ls_status;

        if (currentSecond == Il_offset && Ib_processing_completed)
        {
            //Main processing method
            ue_action();
        }

    }

在 Tick 事件中,我在 PageLoad 中填充了一个 offset 的实例变量,在这种情况下它将是 1 或 31,并在处理成功后在 ue_action 事件中将布尔变量设置为 true。

为了摆脱原来的问题,不做递归编程和循环,推荐使用 Tick 事件来代替,满足我的需求。

谢谢,.NET 学习曲线开始了。

威廉。

【讨论】:

    猜你喜欢
    • 2012-07-26
    • 2013-08-13
    • 2016-02-16
    • 2014-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-25
    • 1970-01-01
    相关资源
    最近更新 更多