【问题标题】:C#: Is it right to use Interlocked to count the number of calls of a delegate?C#:使用 Interlocked 来统计委托的调用次数是否正确?
【发布时间】:2020-10-13 09:07:41
【问题描述】:

我正在玩 Interlocked.Increment 并且想知道哪个呼叫计数器 impl。下面(基本上是获取给定委托的调用次数)在高度并发的环境中正常工作(即线程安全)。

public interface ICallCounter<in TInput, out TOutput>
{
    public TOutput Invoke(TInput input);
    public int Count { get; }
}
public class InterlockedPreCallCounter<TInput, TOutput> : ICallCounter<TInput, TOutput>
{
    private readonly Func<TInput, TOutput> _func;
    private int _count;
    public int Count => _count;

    public InterlockedPreCallCounter(Func<TInput, TOutput> func) => _func = func;

    public TOutput Invoke(TInput input)
    {
        Interlocked.Increment(ref _count);
        // What if there is an interruption / crash at some point here?
        return _func(input);
    }

}
public class InterlockedPostCallCounter<TInput, TOutput>
{
    private readonly Func<TInput, TOutput> _func;
    private int _count;
    public int Count => _count;
    public InterlockedPostCallCounter(Func<TInput, TOutput> func) => _func = func;

    public TOutput Invoke(TInput input)
    {
        var result = _func(input);
        Interlocked.Increment(ref _count);
        return result;
    }
}
public class LockCallCounter<TInput, TOutput> : ICallCounter<TInput, TOutput>
{
    private readonly Func<TInput, TOutput> _func;
    public int Count { get; private set; }
    private readonly object _syncRoot = new object();

    public LockCallCounter(Func<TInput, TOutput> func) => _func = func;

    public TOutput Invoke(TInput input)
    {
        lock (_syncRoot)
        {
            var result = _func(input);
            Count++;
            return result;
        }
    }
}

【问题讨论】:

  • 这取决于您要计算的内容。调用前实现统计Invoke被调用的次数,不管是否失败,调用后实现统计调用Invoke的成功次数。 LockCall 实现与您的调用后版本相同,只是它将所有对_func 的调用序列化
  • @Sean 也许应该这样说,是否有可能程序崩溃然后捕获抛出的异常,因此哪个是“最坚固”的实现。
  • "Tjhe LockCall 实现与您的调用后版本相同,只是它序列化了对 _func 的所有调用" wdym?
  • 这取决于您要测量的内容。他们都同样坚固,但每个人都做一些与其他人不同的事情。
  • LockCall 中一次只有一个线程可以调用Invoke,因为你已经锁定了它。在您的其他实现中,多个线程可以调用它。

标签: c# .net-core concurrency locking interlocked-increment


【解决方案1】:

相对于_count,所有方法都是完全线程安全的。

但是,无论_func 是否引发异常,这都会增加_count

Interlocked.Increment(ref _count);
return _func(input);

仅当_func 不引发异常时,这将增加_count

var result = _func(input);
Interlocked.Increment(ref _count);
return result;

这将与上述相同,但在多线程环境中性能较差,特别是因为任何时候只有一个线程能够调用_func

lock (_syncRoot)
{
    var result = _func(input);
    Count++;
    return result;
}

您选择哪种取决于您要测量的内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-26
    • 2018-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    相关资源
    最近更新 更多