C# 使用IEnumerable源进行分区

C# 使用IEnumerable源进行分区,c#,async-await,task-parallel-library,producer-consumer,C#,Async Await,Task Parallel Library,Producer Consumer,我有类型为IProducerConsumerCollection的ConcurrentQueue,即 IProducerConsumerCollection<Job> _queue = new ConcurrentQueue<Job>(); IProducerConsumerCollection\u queue=new ConcurrentQueue(); 生产者方法将作业添加到_队列,消费者方法处理来自_队列的作业。现在在consumer方法中,我喜欢同时处理作业。

我有类型为IProducerConsumerCollection的ConcurrentQueue,即

IProducerConsumerCollection<Job> _queue = new ConcurrentQueue<Job>();
IProducerConsumerCollection\u queue=new ConcurrentQueue();
生产者方法将作业添加到_队列,消费者方法处理来自_队列的作业。现在在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; }
}
公共类测试队列
{
IProducerConsumerCollection_queue=new ConcurrentQueue();
私有静态HttpClient _client=new HttpClient();
公共测试队列()
{
WorkProducerThread();
WorkConsumerThread();
}
public void WorkConsumerThread()
{
如果(_queue.Count>0)
{
//此时,创建了4个分区,但所有记录仅在第1个分区中;2、3、4个分区为空
var partitioner=partitioner.Create(_queue).GetPartitions(4);
Task t=Task.WhenAll(
从分区器中的分区
选择Task.Run(异步()=>
{
使用(分区)
{
while(partition.MoveNext())
等待CreateJobs(partition.Current);
}
}));
t、 等待();
//此时,队列计数仍为20,如何在处理时从队列集合中删除项目?
}
}
专用异步任务CreateJobs(作业作业)
{
HttpContent bodyContent=null;
等待_client.PostAsync(“作业”,bodyContent);
}
public void WorkProducerThread()
{
如果(_queue.Count==0)
{
尝试
{
对于(int i=0;i<20;i++)
{
Job Job=new Job{Id=i,JobName=“j”+i.ToString(),JobCreated=DateTime.Now};
_queue.TryAdd(作业);
}
}
捕获(例外情况除外)
{
//_Log.Error(“将作业添加到集合时出现异常”,例如);
}
}
}
}
公开课工作
{
公共int Id{get;set;}
公共字符串JobName{get;set;}
已创建公共日期时间作业{get;set;}
}
有两个问题,

  • Partitioner.Create(_queue).GetPartitions(4);GetPartitions创建4个分区,但所有记录仅在第一个分区中;2、3、4分区为空。我找不到,为什么会这样?理想情况下,所有4个分区每个都应该有5条记录(因为队列中总共有20条记录)。我读了MSDN关于分区的文章,但并没有得到任何线索。我还检查了文章中的分区示例

  • 另外,我想在consumer方法中处理后从_队列中删除该项目,只有一种方法_queue.TryTake方法可以删除该项目。我不知道如何删除项目和分区

  • 我可以考虑用任何其他方法来获得同样的结果。

    提前谢谢

    创建(_queue).GetPartitions(4);Partitioner.getpartitions 创建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);
                }
            }
        }));
    
    您会注意到,
    控制台.WriteLine
    会将值从
    0
    写入
    3
    ——表明它们被正确分区

    另外,我想在中处理后从_队列中删除该项目 消费者方法,只有一种方法_queue.TryTake方法 删除项目。我不知道如何删除项目和分区

    您只需稍加重写即可实现这一点。主要的变化是切换到
    BlockingCollection
    ,并通过添加来访问
    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();
            }
        }
    }
    
    使用系统;
    使用System.Collections.Concurrent;
    使用System.Linq;
    使用System.Net.Http;
    使用System.Threading.Tasks;
    名称空间测试
    {
    公共类测试队列
    {
    BlockingCollection_queue=新建BlockingCollection();
    私有静态HttpClient _client=new HttpClient();
    公共测试队列()
    {
    WorkProducerThread();
    WorkConsumerThread();
    }
    public void WorkConsumerThread()
    {
    如果(!\u queue.IsCompleted)
    {
    //此时,创建了4个分区,但所有记录仅在第1个分区中;2、3、4个分区为空
    var partitioner=\u queue.GetConsumingPartitioner().GetPartitions(4);
    Task t=Task.WhenAll(
    从分区器中的分区
    选择Task.Run(异步()=>
    {
    使用(分区)
    {
    while(partition.MoveNext())
    等待CreateJobs(partition.Current);
    }
    }));
    t、 等待();
    Console.WriteLine(_queue.Count);
    }
    }
    专用异步任务CreateJobs(作业作业)
    {
    //HttpContent bodyContent=null;
    //等待_client.PostAsync(“作业”,bodyContent);
    等待任务。延迟(100);
    }
    public void WorkProducerThread()
    {
    如果(_queue.Count==0)
    {
    尝试
    {
    对于(int i=0;i<20;i++)
    {
    Job Job=new Job{Id=i,JobName=“j”+i.ToString(),JobCreated=DateTime.Now};
    _queue.TryAdd(作业);
    }
    _queue.CompleteAdding();
    }
    捕获(例外情况除外)
    {
    //_Log.Error(“将作业添加到集合时出现异常”,例如);
    }