我有类型IProducerConsumerCollection的ConcurrentQueue,即
IProducerConsumerCollection<Job> _queue = new ConcurrentQueue<Job>();
和将方法添加到_queue的producer方法和从_queue处理Job的consumer方法。现在在使用者方法中,我喜欢同时处理作业。下面是带有生产者和消费者方法的样本类的代码:
public class TestQueue
{
IProducerConsumerCollection<Job> _queue = new ConcurrentQueue<Job>();
private static HttpClient _client = new HttpClient();
public TestQueue()
{
WorkProducerThread();
WorkConsumerThread();
}
public void WorkConsumerThread()
{
if (_queue.Count > 0)
{
//At this point, 4 partitions are created but all records are in 1st partition only; 2,3,4 partition are empty
var partitioner = Partitioner.Create(_queue).GetPartitions(4);
Task t = Task.WhenAll(
from partition in partitioner
select Task.Run(async () =>
{
using (partition)
{
while (partition.MoveNext())
await CreateJobs(partition.Current);
}
}));
t.Wait();
//At this point, queue count is still 20, how to remove item from _queue collection when processed?
}
}
private async Task CreateJobs(Job job)
{
HttpContent bodyContent = null;
await _client.PostAsync("job", bodyContent);
}
public void WorkProducerThread()
{
if (_queue.Count == 0)
{
try
{
for (int i = 0; i < 20; i++)
{
Job job = new Job { Id = i, JobName = "j" + i.ToString(), JobCreated = DateTime.Now };
_queue.TryAdd(job);
}
}
catch (Exception ex)
{
//_Log.Error("Exception while adding jobs to collection", ex);
}
}
}
}
public class Job
{
public int Id { get; set; }
public string JobName { get; set; }
public DateTime JobCreated { get; set; }
}
有两个问题,
Partitioner.Create(_queue).GetPartitions(4); Partitioner.GetPartions
创建4个分区但所有记录仅在第一个分区中; 2,3,4分区是空的。我找不到,为什么会这样?理想情况下,所有4个分区每个应该有5个记录(因为总共有20个记录在队列中)。我在MSDN上阅读了有关分区的this文章,但没有得到任何线索。我还检查了this文章中的分区示例。
此外,我想在使用consumer方法处理后从_queue中删除该项目,并且只有一种方法_queue.TryTake方法来删除项目。我不知道如何删除项目以及分区?
我可以考虑任何替代方法来实现相同的结果。
提前致谢。
答案 0 :(得分:1)
Partitioner.Create(_queue).GetPartitions(4); Partitioner.GetPartions 创建4个分区但所有记录仅在第一个分区中; 2,3,4 分区是空的。
这不正确,您的队列条目正在正确分区。要进行验证,请稍微更改处理逻辑以记录正在执行工作的分区:
Task t = Task.WhenAll(
from partition in partitioner.Select((jobs, i) => new { jobs, i })
select Task.Run(async () =>
{
using (partition.jobs)
{
while (partition.jobs.MoveNext())
{
Console.WriteLine(partition.i);
await CreateJobs(partition.jobs.Current);
}
}
}));
您会注意到Console.WriteLine
会将值从0
写入3
- 表明它们正在被正确分区。
另外,我想在处理之后从_queue中删除该项目 消费者方法和_queue.TryTake方法只有一种方法 除去项目。我不知道如何删除项目以及分区?
您可以通过轻微重写来实现这一目标。主要更改是切换为BlockingCollection
并添加this NuGet package以访问GetConsumingPartitioner
。
尝试一下:
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace Test
{
public class TestQueue
{
BlockingCollection<Job> _queue = new BlockingCollection<Job>();
private static HttpClient _client = new HttpClient();
public TestQueue()
{
WorkProducerThread();
WorkConsumerThread();
}
public void WorkConsumerThread()
{
if (!_queue.IsCompleted)
{
//At this point, 4 partitions are created but all records are in 1st partition only; 2,3,4 partition are empty
var partitioner = _queue.GetConsumingPartitioner().GetPartitions(4);
Task t = Task.WhenAll(
from partition in partitioner
select Task.Run(async () =>
{
using (partition)
{
while (partition.MoveNext())
await CreateJobs(partition.Current);
}
}));
t.Wait();
Console.WriteLine(_queue.Count);
}
}
private async Task CreateJobs(Job job)
{
//HttpContent bodyContent = null;
//await _client.PostAsync("job", bodyContent);
await Task.Delay(100);
}
public void WorkProducerThread()
{
if (_queue.Count == 0)
{
try
{
for (int i = 0; i < 20; i++)
{
Job job = new Job { Id = i, JobName = "j" + i.ToString(), JobCreated = DateTime.Now };
_queue.TryAdd(job);
}
_queue.CompleteAdding();
}
catch (Exception ex)
{
//_Log.Error("Exception while adding jobs to collection", ex);
}
}
}
}
public class Job
{
public int Id { get; set; }
public string JobName { get; set; }
public DateTime JobCreated { get; set; }
}
class Program
{
static void Main(string[] args)
{
var g = new TestQueue();
Console.ReadLine();
}
}
}