并发节流

时间:2012-01-27 10:23:31

标签: c# .net multithreading

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

[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--;
        }
    }
}

2 个答案:

答案 0 :(得分:1)

问题是虽然并发性限制为2,但您仍然在限制代码中使用非线程安全对象(MaxRegister):

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

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

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

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

答案 1 :(得分:0)

从我所看到的,对于“ConcurrencyThrottle.Enter”中的初学者,你有:

while (_register.CurrentValue == _max) 

如果CurrentValue大于max,那会破坏,所以也许你应该:

while (_register.CurrentValue >= _max) 

其次,你有

var maxReg = new MaxRegister();
在ThrottleTest方法中,然后在“state”变量中分配和操作 - 但是,此变量与ConcurrencyThrottle类中声明的变量完全无关。因此,在“doWorkState”中递增或递减一个对于在“ConcurrencyThrottle.Enter”中测试的那个没有任何影响。

我很想让Max ConcurrencyThrottle成为一个单身人士并且如此:

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”中操作的那个无关。

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