并发限制

本文关键字:并发 | 更新日期: 2023-09-27 18:21:31

下面是一个简单的并发限制和相关测试。它通常不会观察到比指定的更多的并发性,但我不确定为什么?

[TestFixture]
public class ConcurrencyThrottleTests
{
    [Test]
    public void ThrottleTest()
    {
        var throttle = new ConcurrencyThrottle(2);
        var maxReg = new MaxRegister();
        var threadPool = new SmartThreadPool();
        var state = new DoWorkState {Throttle = throttle, MaxRegister = maxReg};
        var workItemResults = new List<IWaitableResult>();
        for (int i = 0; i < 1000; i++)
            workItemResults.Add(threadPool.QueueWorkItem(DoWork, state));
        SmartThreadPool.WaitAll(workItemResults.ToArray());
        Assert.IsTrue(maxReg.MaxValue <= 2);
    }
    public void DoWork(object state)
    {
        var doWorkState = (DoWorkState)state;
        doWorkState.Throttle.Enter();
        try
        {
            doWorkState.MaxRegister.Increment();
            Thread.Sleep(10);
        }
        finally
        {
            doWorkState.MaxRegister.Decrement();
            doWorkState.Throttle.Exit();
        }
    }
    public class DoWorkState
    {
        public IConcurrencyThrottle Throttle { get; set; }
        public MaxRegister MaxRegister { get; set; }
    }
    public class ConcurrencyThrottle : IConcurrencyThrottle
    {
        private readonly int _max;
        private readonly object _lock = new object();
        private readonly MaxRegister _register = new MaxRegister();
        public ConcurrencyThrottle(int max)
        {
            _max = max;
        }
        public void Exit()
        {
            lock (_lock)
            {
                _register.Decrement();
                Monitor.Pulse(_lock);
            }
        }
        public void Enter()
        {
            lock (_lock)
            {
                while (_register.CurrentValue == _max)
                    Monitor.Wait(_lock);
                _register.Increment();
            }
        }
    }
    public class MaxRegister
    {
        public int MaxValue { get; private set; }
        public int CurrentValue { get; private set; }
        public void Increment()
        {
            MaxValue = Math.Max(++CurrentValue, MaxValue);
        }
        public void Decrement()
        {
            CurrentValue--;
        }
    }
}

并发限制

问题是,尽管并发限制为两个,但您仍然在节流代码中使用非线程安全对象(MaxRegister):

doWorkState.Throttle.Enter();
try
{
    doWorkState.MaxRegister.Increment();
    Thread.Sleep(10);
}
finally
{
    doWorkState.MaxRegister.Decrement();
    doWorkState.Throttle.Exit();
}

MaxRegister.IncrementMaxRegister.Decrement不涉及锁定,并且不使用使它们安全所需的原子Interlocked操作。

MaxRegister.Decrement中使用Interlocked.Decrement就足够了,Increment更难,因为您有两个值。可能您可以在CurrentValue上使用Interlocked.Increment,记住结果,然后在必要时原子地使用CompareExchange来增加MaxValue。或者两种操作都使用锁:)

请注意,为了使用Interlocked,您需要放弃使用自动实现的属性,因为互锁方法具有ref参数。

从我所看到的,对于"ConcurrentThrottle.Enter"中的初学者来说,你有:

while (_register.CurrentValue == _max) 

如果CurrentValue大于max,那么它就会中断,所以也许你应该有:

while (_register.CurrentValue >= _max) 

其次,你有

var maxReg = new MaxRegister();

在ThrottleTest方法中,然后在"state"变量中分配和操作该方法——然而,该变量与ConcurrencyThrottle类中声明的变量完全无关。因此,递增或递减"doWorkState"中的一个对您在"ConcurrentThrottle.Enter"中测试的一个没有任何影响。

我很想将ConcurrentThrottle最大化为一个单例,并将其设置为:

public class ConcurrencyThrottle : IConcurrencyThrottle
{
    private int Max { get; set;}
    private static object _lock = new object();
    private static object _concurrencyLock = new object();
    public static MaxRegister Register { get; set; }
    private static volatile _Default;
    private ConcurrencyThrottle()
    {
        Register = new MaxRegister
        {
            CurrentValue = 0,
            MaxValue = 2
        };
    }
    public static ConcurrencyThrottle Default
    {
        get
        {
            lock (_lock)
            {
                if(_Default == null)
                {
                    _Default = new ConcurrencyThrottle();
                }
                return_Default;
            }
        }
    }
    public void Enter() 
    { 
        lock (_concurrencyLock) 
        { 
            while (Register.CurrentValue == _max) 
                Monitor.Wait(_concurrencyLock); 
            Register.Increment(); 
        } 
    } 
    etc etc

这显然只是一个建议,但据我所知,ConcurrencyThrottle中的MaxRegister与您在"DoWork"中操作的那个无关。

希望这是有帮助的,快乐编码
干杯,
克里斯。