下面是一个简单的并发限制和相关测试。它不能经常观察到超过指定的并发性,但我不确定为什么?
[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--;
}
}
}发布于 2012-01-27 18:47:52
问题是,尽管并发被限制为两个,但您仍然在可调节的代码中使用非线程安全对象( MaxRegister):
doWorkState.Throttle.Enter();
try
{
doWorkState.MaxRegister.Increment();
Thread.Sleep(10);
}
finally
{
doWorkState.MaxRegister.Decrement();
doWorkState.Throttle.Exit();
}MaxRegister.Increment和MaxRegister.Decrement不涉及锁定,也不使用原子Interlocked操作,这是确保它们安全所必需的。
在MaxRegister.Decrement中使用Interlocked.Decrement就足够了,而Increment则更难,因为您有两个值。您可以在CurrentValue上使用Interlocked.Increment,记住结果,然后在必要时自动使用CompareExchange来增加MaxValue。或者只对这两个操作都使用锁:)
请注意,为了使用Interlocked,您需要避免使用自动实现的属性,因为互锁方法有ref参数。
发布于 2012-01-27 18:57:13
在我看来,对于"ConcurrencyThrottle.Enter“的初学者来说,你有:
while (_register.CurrentValue == _max) 如果CurrentValue大于max,它就会中断,所以也许你应该这样做:
while (_register.CurrentValue >= _max) 其次,你有
var maxReg = new MaxRegister();在你的状态方法中,然后在你的“ThrottleTest”变量中赋值和操作-然而,这个变量与在ConcurrencyThrottle类中声明的变量完全无关。因此,递增或递减"doWorkState“中的值对您在"ConcurrencyThrottle.Enter”中测试的值没有任何影响。
我很想把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“中操作的are无关。
希望对大家有帮助,快乐编码!
干杯,
克里斯。
https://stackoverflow.com/questions/9031843
复制相似问题