我正在努力将多个任务添加到并发队列的integeres。我看过一些教程,但我发现它们非常复杂。另外,我想这样做是因为我想实现消费者 - 生产者问题。
这就是我一直想做的事情
class Program
{
const int MAX_VALUE = 20;
const int valueP = 0;
static BlockingCollection<int> bc = new BlockingCollection<int>(new ConcurrentQueue<int>(), 20 );
static void producer(int value)
{
for (int i = 0; i < MAX_VALUE; i++)
{
bc.Add(value);
value++;
Console.WriteLine( value);
Thread.Sleep(2000);
}
Thread.Sleep(2000);
}
static void consuemr()
{
int item = 0;
item = bc.Take();
Console.WriteLine("An item has been consumed", item);
Thread.Sleep(2000);
}
static void Main(string[] args)
{
Task t1 =Task.Run(() => producer(valueP));
Task t2 = Task.Run(() => producer(valueP));
Task.WaitAll(t1, t2);
Task.Run(() =>consuemr());
}
}
但是当它引领消费者时,事情就会停止工作
答案 0 :(得分:1)
根据BlockingCollection
的指定构造函数,您可以拥有20个项目的限制。
你输入了40个项目,所以在添加了20个项目之后,方法将被阻止。
尝试将构造函数更改为
static BlockingCollection<int> bc = new BlockingCollection<int>(new ConcurrentQueue<int>(), 100);
然后你会发现它会起作用。
要解决此问题,您需要确保消费者在生产者添加物品时正在取物品。
由于你对生产者做了Task.WaitAll
,它会等到所有项目都被添加(这种情况永远不会发生,因为在添加了20个项目后,下一次调用Add()
方法会阻塞,请参阅备注https://msdn.microsoft.com/en-us/library/dd287137(v=vs.110).aspx)部分。
您需要重新设计代码,以便消费者在生产者添加项目时使用项目,或确保BlockingCollection
的上限大于您要添加的项目数在服用它们之前。
除了您当前的代码,请替换最后一行
Task.Run(() =>consuemr());
在
Task.Run(() =>consuemr()).Wait();
我在非生产代码中使用.Wait()或.WaitAll()不是问题,但在生产代码中你最好使用async ..等待。
完整示例
我希望我的提示能给你一些如何解决的想法。试试这个:
class Program
{
const int MAX_VALUE = 20;
const int valueP = 0;
static BlockingCollection<int> bc = new BlockingCollection<int>(new ConcurrentQueue<int>(), 20);
static void producer(int value)
{
for (int i = 0; i < MAX_VALUE; i++)
{
bc.Add(value);
value++;
Console.WriteLine("Producing value {0}", value);
Thread.Sleep(20);
}
Thread.Sleep(20);
}
static void Consumer()
{
foreach(var item in bc.GetConsumingEnumerable())
{
Console.WriteLine("An item has been consumed: {0}", item);
}
}
static void Main(string[] args)
{
Task t1 =Task.Run(() => producer(valueP));
Task t2 = Task.Run(() => producer(valueP));
Task t3 = Task.Run(() => Consumer());
Task.WaitAll(t1, t2);
bc.CompleteAdding(); // signal end of producing values
t3.Wait(); // wait for consumer to read any unread values
}
}
现在正在单独的线程上使用这些项目,而生产者正在添加项目,因此未达到上限。
另一种选择是,不是使用bc.CompleteAdding();
和GetConsumingEnumerable()
,而是使用GetConsumingEnumerable
取CancellationToken
作为参数来停止根据用户输入使用项目示例
此外,这也可以(替换现有代码):
static void Consumer()
{
int item;
while(!bc.IsCompleted)
{
item = bc.Take();
Console.WriteLine("An item has been consumed: {0}", item);
Thread.Sleep(20);
}
}
static void Main(string[] args)
{
Task t1 =Task.Run(() => producer(valueP));
Task t2 = Task.Run(() => producer(valueP));
Task t3 = Task.Run(() => Consumer());
Task.WaitAll(t1, t2);
bc.CompleteAdding(); // signal end of producing values
t3.Wait(); // wait for consumer to read any unread values
}
我希望这会给你一个更好的主意。如果您有一个现实世界的问题需要解决,请解释您还需要什么。因为有很多方法可以从阻塞集合中读取,但这取决于您的具体情况。