不确定C#代码是否存在计时/争用条件?(使用.NET fiddle repo)

iezvtpos  于 2023-03-09  发布在  .NET
关注(0)|答案(1)|浏览(124)

我试图测试我的自定义IProgress是否正在做我期望它做的事情-〉每秒“报告”当前状态。
我有一些代码,但总是失败,但这是一个问题--只有当我在测试用例中几乎没有任何代码时,它才会失败,我不知道如何真正解释这一点。
如果我在测试中再添加一段代码(在repo中注解掉了),如果你取消注解,它就可以工作。
这就像..需要发射一些东西..但在检查“Assert”之前还没有完成?
完整报告:https://dotnetfiddle.net/qXHzlx
下面是代码:

// Arrange.
var reportValue = 0;
var progress = new TimerProgress<int>((int value) => reportValue = value);

await Task.Delay(TimeSpan.FromSeconds(2)); // Need to wait more than 1 second for the report to work.

// Act.
progress.Report(5);

// Assert.
reportValue.ShouldBe(5);

和自定义的TimerProgress

public class TimerProgress<T> : IProgress<T>
{
    private readonly TimeSpan _intervalInMilliseconds;
    private readonly IProgress<T> _progress;
    private DateTime? _lastReportedOn;

    public TimerProgress(Action<T> handler, double intervalInMillieconds = 1000)
    {
        if (intervalInMillieconds <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(intervalInMillieconds));
        }

        _intervalInMilliseconds = TimeSpan.FromMilliseconds(intervalInMillieconds);

        _progress = new Progress<T>(handler);

        _lastReportedOn = DateTime.UtcNow;
    }

    public void Report(T value)
    {
        var now = DateTime.UtcNow;

        if (now - _lastReportedOn > _intervalInMilliseconds)
        {
            // We're due for a report!
            _progress.Report(value);
        }

        _lastReportedOn = now;
    }
}

对于测试用例,我期望:

  • “记住”新的TimeProgress示例。
  • 等待2秒。(你会看到为什么在滴答声)
  • “Report”。现在检查remembered-now是否超过1秒。它 * 应该是 *,因为我们已经等待了2秒!所以现在,我们应该“report”
  • 处理程序被调用..它“记住”数字5
  • 我们现在Assert“5”最终被报告。

因此,我不断收到一个失败Assert,说值是0,而不是5
但是当我向测试中再添加一行代码时,该值现在是5
这是关于定时还是事件触发的问题?
这是关于我的处理程序如何试图更新处理程序 * 外部 * 的变量,也就是Bad™吗️?
编辑1:有人建议说Progress<T>实际上在引擎盖下使用了同步上下文,这就是问题所在?

ecfdbz9o

ecfdbz9o1#

This SO question已经就这个问题进行了很好的讨论。
因为进程运行在SynchronizationContext上,所以他们建议不要在我自己的类中使用Progress<T>,而是 * 直接 * 调用Action<T>,这意味着它将在您当前的上下文中发生。
例如(被盗代码)

public class SynchronousProgress<T> : IProgress<T>
{
    private readonly Action<T> action;

    public SynchronousProgress(Action<T> action)
    {
        this.action = action;
    }

    public void Report(T value)
    {
        action(value);
    }
}

相关问题