我有以下代码抛出SemaphoreFullException
,我不明白为什么?
如果我将_semaphore = new SemaphoreSlim(0, 2)
更改为
_semaphore = new SemaphoreSlim(0, int.MaxValue)
然后一切正常。 任何人都可以找到这个代码的错误并向我解释。
class BlockingQueue<T>
{
private Queue<T> _queue = new Queue<T>();
private SemaphoreSlim _semaphore = new SemaphoreSlim(0, 2);
public void Enqueue(T data)
{
if (data == null) throw new ArgumentNullException("data");
lock (_queue)
{
_queue.Enqueue(data);
}
_semaphore.Release();
}
public T Dequeue()
{
_semaphore.Wait();
lock (_queue)
{
return _queue.Dequeue();
}
}
}
public class Test
{
private static BlockingQueue<string> _bq = new BlockingQueue<string>();
public static void Main()
{
for (int i = 0; i < 100; i++)
{
_bq.Enqueue("item-" + i);
}
for (int i = 0; i < 5; i++)
{
Thread t = new Thread(Produce);
t.Start();
}
for (int i = 0; i < 100; i++)
{
Thread t = new Thread(Consume);
t.Start();
}
Console.ReadLine();
}
private static Random _random = new Random();
private static void Produce()
{
while (true)
{
_bq.Enqueue("item-" + _random.Next());
Thread.Sleep(2000);
}
}
private static void Consume()
{
while (true)
{
Console.WriteLine("Consumed-" + _bq.Dequeue());
Thread.Sleep(1000);
}
}
}
答案 0 :(得分:0)
如果您想使用信号量来控制并发线程的数量,那么您使用它是错误的。当您将项目出列时,您应该获取信号量,并在线程完成处理该项目时释放信号量。
您现在拥有的是一个系统,任何时候只允许两个项目在队列中。最初,您的信号量计数为2.每次排队项目时,计数都会减少。在两个项目之后,计数为0,如果您尝试再次发布,您将获得信号量完全异常。
如果您确实希望使用信号量执行此操作,则需要从Release
方法中删除Enqueue
调用。并向Release
类添加BlockingQueue
方法。然后你会写:
private static void Consume()
{
while (true)
{
Console.WriteLine("Consumed-" + _bq.Dequeue());
Thread.Sleep(1000);
bq.Release();
}
}
这会使你的代码工作,但它不是一个很好的解决方案。一个更好的解决方案是使用BlockingCollection<T>
和两个持久消费者。类似的东西:
private BlockingCollection<int> bq = new BlockingCollection<int>();
void Test()
{
// create two consumers
var c1 = new Thread(Consume);
var c2 = new Thread(Consume);
c1.Start();
c2.Start();
// produce
for (var i = 0; i < 100; ++i)
{
bq.Add(i);
}
bq.CompleteAdding();
c1.Join();
c2.Join();
}
void Consume()
{
foreach (var i in bq.GetConsumingEnumerable())
{
Console.WriteLine("Consumed-" + i);
Thread.Sleep(1000);
}
}
这为你提供了两个消耗这些项的持久线程。好处是您可以避免为每个项目启动新线程(或让RTL为池线程分配)的成本。相反,线程在队列上执行非忙等待。您也不必担心显式锁定等问题。代码更简单,更健壮,并且不太可能包含错误。