并发限制
本文关键字:并发 | 更新日期: 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.Increment
和MaxRegister.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"中操作的那个无关。
希望这是有帮助的,快乐编码
干杯,
克里斯。