以下是简单的并发限制和相关测试。它通常不会比观察到的更多并发工作,但我不确定为什么?
[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--;
}
}
}
答案 0 :(得分:1)
问题是虽然并发性限制为2,但您仍然在限制代码中使用非线程安全对象(MaxRegister
):
doWorkState.Throttle.Enter();
try
{
doWorkState.MaxRegister.Increment();
Thread.Sleep(10);
}
finally
{
doWorkState.MaxRegister.Decrement();
doWorkState.Throttle.Exit();
}
MaxRegister.Increment
和MaxRegister.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();
我很想让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”中操作的那个无关。
希望有帮助,快乐编码!
干杯,
克里斯。