C# 在TPL数据流中,是否有一种方法一次只能处理一个链接块中的一个任务?

C# 在TPL数据流中,是否有一种方法一次只能处理一个链接块中的一个任务?,c#,tpl-dataflow,C#,Tpl Dataflow,我有两个类,serviceclient和一个service。serviceclient将生成由服务处理的消息。ServiceClient消息应严格按照FIFO顺序处理,下一条消息仅在前一条消息处理完毕后可用 为了解决这个问题,我在每个serviceclient中放置了一个操作块,该操作块直接调用服务来处理客户端消息,我认为这很好,但需要额外的依赖注入。我想知道是否有一种方法可以设置它,使服务可以直接链接到serviceclient messageblock,以便它可以同时处理来自多个servic

我有两个类,serviceclient和一个service。serviceclient将生成由服务处理的消息。ServiceClient消息应严格按照FIFO顺序处理,下一条消息仅在前一条消息处理完毕后可用

为了解决这个问题,我在每个serviceclient中放置了一个操作块,该操作块直接调用服务来处理客户端消息,我认为这很好,但需要额外的依赖注入。我想知道是否有一种方法可以设置它,使服务可以直接链接到serviceclient messageblock,以便它可以同时处理来自多个serviceclient消息块的消息,但每次只能处理来自任何特定serviceclient的一条消息

具有所需功能的某些代码::

    static void Main(string[] args)
    {
        var service = new Service();

        //I would like messages from these clients to be processed concurrently by service, but only one at a time per client. 
        //So if Client A has two messages in queue(a1, a2) and B has 3(b1,b2,b3), it will immediately take a1&b1. If a1 finishes, it will then take a2. if b1 finishes it will take b2, same with b2 and b3. It would never process a1 concurrently with a2, or b1 concurrently with b2 or b3. 
        service.AddClient(new ServiceClient());
        service.AddClient(new ServiceClient());
    }

    interface IServiceMessage
    {
        string Message { get; }
    }

    class ServiceClient
    {
        public BufferBlock<IServiceMessage> clientServiceMsgs = new BufferBlock<IServiceMessage>();

        public ServiceClient() {
            //run task to populate bufferblock 
        }
    }

    class Service
    {
        ActionBlock<IServiceMessage> processServiceMsgsBlock;

        public Service() {
            processServiceMsgsBlock = new ActionBlock<IServiceMessage>(ProcessServiceMessage);
        }  

        public async Task ProcessServiceMessage(IServiceMessage msg) {
            //process stuff
            return;
        }

        public void AddClient(ServiceClient client)
        {
            client.clientServiceMsgs.LinkTo(processServiceMsgsBlock);
        }
    }
static void Main(字符串[]args)
{
var服务=新服务();
//我希望这些客户端的消息由服务同时处理,但每个客户端一次只能处理一条消息。
//所以,如果客户机A在队列中有两条消息(a1、a2),而B有3条消息(b1、b2、b3),它将立即接受a1和b1。如果a1完成,它将接受a2。如果b1完成,它将接受b2,与b2和b3相同。它决不会与a2同时处理a1,或与b2或b3同时处理b1。
AddClient(新的ServiceClient());
AddClient(新的ServiceClient());
}
接口IServiceMessage
{
字符串消息{get;}
}
类服务客户端
{
public BufferBlock clientServiceMsgs=new BufferBlock();
公共服务客户机(){
//运行任务以填充缓冲区块
}
}
班级服务
{
ActionBlock ProcessServiceMsgBlock;
公共服务(){
ProcessServiceMsgBlock=新操作块(ProcessServiceMessage);
}  
公共异步任务ProcessServiceMessage(IServiceMessage消息){
//加工材料
返回;
}
public void AddClient(ServiceClient客户端)
{
clientServiceMsgs.LinkTo(processservicemsgblock);
}
}

您真的需要这里的第三方物流数据流吗?我会给你两个选择,没有,然后最后一个与第三方物流数据流像你问

每个期权共享以下信息

public class ServiceClient
{
    public async Task<IServiceMessage> GetAsync(string filter)
    {
        await Task.Yield();
        return new ServiceMessage() { Message = Guid.NewGuid().ToString() };
    }
}

public class Service
{
    public async Task ProcessAsync(IServiceMessage message)
    {
        // do something with it
        await Task.Delay(10);
    }
}

public interface IServiceMessage
{
    string Message { get; }
}

public class ServiceMessage : IServiceMessage
{
    public string Message { get; set; }
}
公共类服务客户端
{
公共异步任务GetAsync(字符串筛选器)
{
等待任务;
返回新ServiceMessage(){Message=Guid.NewGuid().ToString()};
}
}
公务舱服务
{
公共异步任务进程异步(IServiceMessage消息)
{
//用它做点什么
等待任务。延迟(10);
}
}
公共接口iSeries消息
{
字符串消息{get;}
}
公共类服务消息:IServiceMessage
{
公共字符串消息{get;set;}
}
选项1-服务启动读取和写入数据的任务。只需在第三个服务中将服务和ServiceClient耦合在一起

class Program
{
    static void Main(string[] args)
    {
        var cts = new CancellationTokenSource();
        var service = new Service();
        var serviceClient = new ServiceClient();
        var processor = new ProducerConsumerService(serviceClient, service);
        processor.Process("A", cts.Token);
        processor.Process("B", cts.Token);
        processor.Process("C", cts.Token);
        processor.Process("D", cts.Token);

        Console.WriteLine("Press any key to shutdown");
        Console.Read();

        cts.Cancel();
        processor.WaitForCompletion();
    }
}

public class ProducerConsumerService
{
    private List<Task> _processTasks;
    private ServiceClient _serviceClient;
    private Service _service;

    public ProducerConsumerService(ServiceClient serviceClient, Service service)
    {
        _serviceClient = serviceClient;
        _service = service;

        _processTasks = new List<Task>();
    }

    public void Process(string filter, CancellationToken token)
    {
        _processTasks.Add(Task.Run(() =>
        {
            while (!token.IsCancellationRequested)
            {
                var message = _serviceClient.Get(filter);
                _service.Process(message);
            }
        }));
    }

    public void WaitForCompletion()
    {
        Task.WaitAll(_processTasks.ToArray(), TimeSpan.FromSeconds(10));
    }
}
类程序
{
静态void Main(字符串[]参数)
{
var cts=新的CancellationTokenSource();
var服务=新服务();
var serviceClient=新serviceClient();
var处理器=新的ProducerConsumerService(服务客户机,服务);
处理器进程(“A”,cts.Token);
处理器。进程(“B”,cts。令牌);
处理器.Process(“C”,cts.Token);
处理器。进程(“D”,cts。令牌);
Console.WriteLine(“按任意键关机”);
Console.Read();
cts.Cancel();
processor.WaitForCompletion();
}
}
公共类ProducerConsumerService
{
私有列表processTasks;
私人服务客户(ServiceClient);;
私人服务;;
公共产品消费服务(服务客户端服务客户端,服务服务)
{
_serviceClient=serviceClient;
_服务=服务;
_processTasks=新列表();
}
公共作废进程(字符串筛选器、CancellationToken令牌)
{
_processTasks.Add(Task.Run)(()=>
{
而(!token.IsCancellationRequested)
{
var message=\u serviceClient.Get(过滤器);
_服务流程(消息);
}
}));
}
公共作废等待完成()
{
Task.WaitAll(_processTasks.ToArray(),TimeSpan.FromSeconds(10));
}
}
选项2-与选项1相同,但有两个任务和一个BlockingCollection,它在生产者(ServiceClient)和消费者(Service)之间提供一个有界缓冲区

公共类ProducerBufferConsumerService
{
私人名单(生产任务);;
私人列表(u consumertask);;
私人服务客户(ServiceClient);;
私人服务;;
公共产品BufferConsumerService(服务客户端服务客户端,服务服务)
{
_serviceClient=serviceClient;
_服务=服务;
_producerTasks=新列表();
_consumerTasks=新列表();
}
公共作废处理(取消令牌)
{
var buffer=新的BlockingCollection(1000);
_producerTasks.Add(Task.Run)(异步()=>
{
而(!token.IsCancellationRequested)
{
var message=wait_serviceClient.GetAsync();
添加(消息、令牌);
}
buffer.CompleteAdding();
}));
_consumerTasks.Add(Task.Run)(异步()=>
{
而(!token.IsCancellationRequested&!buffer.IsAddingCompleted)
{
var message=buffer.Take(令牌);
wait_service.ProcessAsync(消息);
}
}));
}
公共作废等待完成()
{
Task.WaitAll(_producertask.ToArray(),10000);
Task.WaitAll(_consumertask.ToArray(),10000);
}
}
选项3-并行处理,同时保持g的顺序
public class ProducerBufferConsumerService
{
    private List<Task> _producerTasks;
    private List<Task> _consumerTasks;
    private ServiceClient _serviceClient;
    private Service _service;

    public ProducerBufferConsumerService(ServiceClient serviceClient, Service service)
    {
        _serviceClient = serviceClient;
        _service = service;

        _producerTasks = new List<Task>();
        _consumerTasks = new List<Task>();
    }

    public void Process(CancellationToken token)
    {
        var buffer = new BlockingCollection<IServiceMessage>(1000);
        _producerTasks.Add(Task.Run(async () =>
        {
            while (!token.IsCancellationRequested)
            {
                var message = await _serviceClient.GetAsync();
                buffer.Add(message, token);
            }

            buffer.CompleteAdding();
        }));

        _consumerTasks.Add(Task.Run(async () =>
        {
            while (!token.IsCancellationRequested && !buffer.IsAddingCompleted)
            {
                var message = buffer.Take(token);
                await _service.ProcessAsync(message);
            }
        }));
    }

    public void WaitForCompletion()
    {
        Task.WaitAll(_producerTasks.ToArray(), 10000);
        Task.WaitAll(_consumerTasks.ToArray(), 10000);
    }
}
class Program
{
    static void Main(string[] args)
    {
        var cts = new CancellationTokenSource();
        var filters = new List<string>() { "A", "B", "C", "D" };
        var service = new Service();
        var serviceClient = new ServiceClient();
        var partitioningService = new PartitioningService(serviceClient, service);
        var processingTask = Task.Run(() => partitioningService.Process(filters, cts.Token));


        Console.WriteLine("Press any key to shutdown");
        Console.ReadKey();

        cts.Cancel();
        processingTask.Wait(10000);
    }
}

public interface IServiceMessage
{
    string Message { get; }
    Guid Id { get; set; }
}

public class ServiceMessage : IServiceMessage
{
    public string Message { get; set; }
    public Guid Id { get; set;  }
}

public class RoutedMessage
{
    public IServiceMessage Message { get; set; }
    public int PartitionId { get; set; }
}

public class PartitioningService
{
    private ServiceClient _serviceClient;
    private Service _service;

    public PartitioningService(ServiceClient serviceClient, Service service)
    {
        _serviceClient = serviceClient;
        _service = service;
    }

    public void Process(List<string> filters, CancellationToken token)
    {
        var linkOptions = new DataflowLinkOptions { PropagateCompletion = true };
        Func<IServiceMessage, RoutedMessage> partitioner = x => new RoutedMessage
            { 
                Message = x,
                PartitionId = x.Id.GetHashCode() / 1000000000
        };

        var partitionerBlock = new TransformBlock<IServiceMessage, RoutedMessage>(partitioner);
        var actionBlock1 = new ActionBlock<RoutedMessage>(async (RoutedMessage msg) => await _service.ProcessAsync(msg));
        var actionBlock2 = new ActionBlock<RoutedMessage>(async (RoutedMessage msg) => await _service.ProcessAsync(msg));
        var actionBlock3 = new ActionBlock<RoutedMessage>(async (RoutedMessage msg) => await _service.ProcessAsync(msg));
        var actionBlock4 = new ActionBlock<RoutedMessage>(async (RoutedMessage msg) => await _service.ProcessAsync(msg));
        var actionBlock5 = new ActionBlock<RoutedMessage>(async (RoutedMessage msg) => await _service.ProcessAsync(msg));
        var actionBlock6 = new ActionBlock<RoutedMessage>(async (RoutedMessage msg) => await _service.ProcessAsync(msg));
        var actionBlock7 = new ActionBlock<RoutedMessage>(async (RoutedMessage msg) => await _service.ProcessAsync(msg));
        var actionBlock8 = new ActionBlock<RoutedMessage>(async (RoutedMessage msg) => await _service.ProcessAsync(msg));
        var actionBlock9 = new ActionBlock<RoutedMessage>(async (RoutedMessage msg) => await _service.ProcessAsync(msg));

        partitionerBlock.LinkTo(actionBlock1, linkOptions, msg => msg.PartitionId == -4);
        partitionerBlock.LinkTo(actionBlock1, linkOptions, msg => msg.PartitionId == -3);
        partitionerBlock.LinkTo(actionBlock1, linkOptions, msg => msg.PartitionId == -2);
        partitionerBlock.LinkTo(actionBlock1, linkOptions, msg => msg.PartitionId == -1);
        partitionerBlock.LinkTo(actionBlock1, linkOptions, msg => msg.PartitionId == 0);
        partitionerBlock.LinkTo(actionBlock1, linkOptions, msg => msg.PartitionId == 1);
        partitionerBlock.LinkTo(actionBlock1, linkOptions, msg => msg.PartitionId == 2);
        partitionerBlock.LinkTo(actionBlock1, linkOptions, msg => msg.PartitionId == 3);
        partitionerBlock.LinkTo(actionBlock1, linkOptions, msg => msg.PartitionId == 4);

        var tasks = new List<Task>();
        foreach (var filter in filters)
        {
            tasks.Add(Task.Run(async () =>
                {
                    Guid filterId = Guid.NewGuid();

                    while (!token.IsCancellationRequested)
                    {
                        var message = await _serviceClient.GetAsync(filter);
                        message.Id = filterId;
                        await partitionerBlock.SendAsync(message);
                    }
                }));
        }

        while (!token.IsCancellationRequested)
            Thread.Sleep(100);

        partitionerBlock.Complete();
        actionBlock1.Completion.Wait();
        actionBlock2.Completion.Wait();
        actionBlock3.Completion.Wait();
        actionBlock4.Completion.Wait();
        actionBlock5.Completion.Wait();
        actionBlock6.Completion.Wait();
        actionBlock7.Completion.Wait();
        actionBlock8.Completion.Wait();
        actionBlock9.Completion.Wait();

        Task.WaitAll(tasks.ToArray(), 10000);
    }
}