如何在调试器暂停代码时忽略时间的推移?

时间:2016-09-08 19:02:54

标签: c# debugging

我正在进行计算,如果运行时间太长,我需要超时。我可能会将超时设置为5秒,并在整个代码中定期进行轮询。实际代码要复杂得多,并且递归很多并且分布在很多类中,但这应该可以大致了解它是如何工作的。基本上每当调用递归或执行可能需要时间的事情时,它都会调用AssertTimeout()。

private DateTime endTime;
public void PerpareTimeout(int timeoutMilliseconds)
{
    endTime = DateTime.UtcNow.AddMilliseconds(timeoutMilliseconds);
}

public void AssertTimeout()
{
    if (DateTime.UtcNow > endTime)
        throw new TimeoutException();
}

public void DoWork1()
{
    foreach (var item in workItems)
    {
        AssertTimeout();
        DoWork2(item)
    }
}
public void DoWork2(WorkItem item)
{
    foreach (var item in workItems)
    {
        AssertTimeout();
        // ...
    }
}

所以当我连接调试器并暂停执行时会出现问题。我想以某种方式禁用超时暂停的时间。因此,如果它运行2秒钟,我点击一个断点并等待5分钟,然后恢复,它将在恢复执行后再运行3秒钟,然后超时。

我可以使用这样的东西:

public void PrepareTimeout(int timeoutMilliseconds)
{
    if (System.Diagnostics.Debugger.IsDebuggerAttached)
        endTime = DateTime.MaxValue;
    else
        endTime = DateTime.UtcNow.AddMilliseconds(timeoutMilliseconds);
}

但是,当程序在调试环境中运行时,这基本上会给出无限超时,如果我不暂停它,我希望它能正常超时。

有没有办法测量经过的时间而不计算调试器暂停的时间?

3 个答案:

答案 0 :(得分:2)

理想情况下,您希望在代码暂停时触发某些事件。由于调试器不会触发事件,因此需要一种解决方法,如Matias所述。

他的解决方案要求您知道应该开始/停止秒表的确切位置,我将在此基础上进行构建。此解决方案的缺点是它需要以足够高的分辨率为您的需求进行持续轮询(这可能对您运行的其他应用程序造成不利影响,从而影响您尝试从计时器中检索的任何数字)。

最终目标是我们将进行计算:

"Running time paused" = "Total time taken" - "Total time paused"

我们将尝试跟踪程序上次运行的时间,然后找出程序从调试恢复后下一次主动运行的时间。

public static class ProgramTimer
{
    public static int resolution = 1000; //resolution of time needed in ms
    private static System.Diagnostics.Stopwatch stopwatch;
    private static System.Timers.Timer pollingTimer;
    private static System.TimeSpan lastMeasuredDebugTimespan;

    public static void Start()
    {
        pollingTimer = new System.Timers.Timer();
        pollingTimer.Interval = resolution;
        pollingTimer.Elapsed += timerEvent;
    }

    private static void timerEvent()
    {
        if (System.Diagnostics.Debugger.IsDebuggerAttached) {
            if (stopwatch == null) {
                stopwatch = System.Diagnostics.Stopwatch.StartNew();
            }
        } else {
            if (stopwatch != null) {
                stopwatch.Stop();
                lastMeasuredDebugTime = stopwatch.Elapsed.TotalMilliseconds;
                stopwatch = null;
            }
        }
    }

    public static System.TimeSpan getTimespanInDebug()
    {
        if (lastMeasuredDebugTimespan) {
            return lastMeasuredDebugTimespan;
        }
        return null;
    }
}

PrepareTimeout中你会打电话给: ProgramTimer.Start()

AssertTimeout

public void AssertTimeout()
{
    DateTime newEndTime;
    newEndTime = endTime; 
    if (ProgramTimer.getTimespanInDebug()) {
        newEndTime = endTime.Subtract(ProgramTimer.getTimespanInDebug());
    }
    if (DateTime.UtcNow > newEndTime)
        throw new TimeoutException();
}

希望有所帮助!

答案 1 :(得分:1)

在看到Matias Cicero的回答后,我有了一个想法:

如果连接了调试器,则启动一个看门狗线程,除了休眠之外什么都不做。也许以100毫秒的增量。如果程序暂停,对Thread.Sleep的调用将花费比预期更长的时间,并且可以通过差异增加结束时间。

public class DebugWatchDog : IDisposable
{
    private bool Terminated;
    private Action<DateTime> updateEndTime;
    private DateTime endTime;
    public DebugWatchDog(Action<DateTime> updateEndTime, DateTime endTime)
    {
        if (!System.Diagnostics.Debugger.IsDebuggerAttached)
            return;
        this.updateEndTime = updateEndTime;
        this.endTime = endTime;
        updateEndTime(DateTime.MaxValue);
        var thread = new Thread(Watch);
        thread.Start();
    }
    public void Dispose()
    {
        lock (this)
            Terminated = true;
    }
    private void Watch()
    {
        DateTime priorTime = DateTime.UtcNow;
        while (true)
        {
            lock (this)
                if (Terminated)
                    return;
            Thread.Sleep(100);
            DateTime nextTime = DateTime.UtcNow;
            var diff = nextTime - priorTime;
            if (diff.TotalMilliseconds > 115)
            {
                endTime += diff;
            }
            if (DateTime.UtcNow > endTime)
            {
                updateEndTime(endTime);
                return;
            }
            priorTime = nextTime;
        }
    }
}

答案 2 :(得分:0)

不太好 解决方法。

您必须使用Stopwatch而不是计算当前时间和开始时间之间的差异。

我们假设你有以下代码:

Stopwatch watch = Stopwatch.StartNew();

DoSomething();
DoAnotherThing(); <-- You want to add a breakpoint here
DoFinalThings();

watch.Stop();

您可以在要断点的行之前添加三行代码:

Stopwatch watch = Stopwatch.StartNew();

DoSomething();

watch.Stop();       // Stops measuring time
Debugger.Break();   // Programatically breaks the program at this point
watch.Start();      // Resumes measuring time

DoAnotherThing();
DoFinalThings();

watch.Stop();