C# TPL数据流管道在服务内部持续运行

C# TPL数据流管道在服务内部持续运行,c#,task-parallel-library,.net-4.5,tpl-dataflow,C#,Task Parallel Library,.net 4.5,Tpl Dataflow,例如,我有三个区块: Buffer -> Transform -> Action 我正在运行webapi服务,它将请求中的数据带到缓冲区块。如何创建这样一个将永远运行的管道,而无需在操作块调用Completion()并停止整个管道 如果您需要在应用程序的生命周期中保留管道,而不仅仅是请求,那么您可以使用静态类来保存它。不一定需要在操作块上调用complete。根据您的需要,另一种选择是将应用程序和处理管道分开。它们可以由数据库消息队列或单独的服务器端应用程序分隔 @

例如,我有三个区块:

       Buffer -> Transform -> Action 

我正在运行webapi服务,它将请求中的数据带到缓冲区块。如何创建这样一个将永远运行的管道,而无需在操作块调用Completion()并停止整个管道

如果您需要在应用程序的生命周期中保留管道,而不仅仅是请求,那么您可以使用静态类来保存它。不一定需要在操作块上调用complete。根据您的需要,另一种选择是将应用程序和处理管道分开。它们可以由数据库消息队列或单独的服务器端应用程序分隔

@svick使用TaskCompletionSource来确定管道何时完成了特定项的工作,这一点很好。总而言之,这里有一个可能有用的快速示例:

public class Controller {

    public async Task<int> PostToPipeline(int inputValue) {
        var message = new MessageIn(inputValue);
        MyPipeline.InputBuffer.Post(message);
        return await message.Completion.Task;
    }
}

public class MessageIn {
    public MessageIn(int value) {
        InputValue = value;
        Completion = new TaskCompletionSource<int>();
    }

    public int InputValue { get; set; }
    public TaskCompletionSource<int> Completion { get; set; }
}

public class MessageProcessed {
    public int ProcessedValue { get; set; }
    public TaskCompletionSource<int> Completion { get; set; }
}

public static class MyPipeline {

    public static BufferBlock<MessageIn> InputBuffer { get; private set; }
    private static TransformBlock<MessageIn, MessageProcessed> transform;
    private static ActionBlock<MessageProcessed> action;

    static MyPipeline() {
        BuildPipeline();
        LinkPipeline();

    }

    static void BuildPipeline() {
        InputBuffer = new BufferBlock<MessageIn>();

        transform = new TransformBlock<MessageIn, MessageProcessed>((Func<MessageIn, MessageProcessed>)TransformMessage, new ExecutionDataflowBlockOptions() {
            MaxDegreeOfParallelism = Environment.ProcessorCount,
            BoundedCapacity = 10
        });

        action = new ActionBlock<MessageProcessed>((Action<MessageProcessed>)CompletedProcessing, new ExecutionDataflowBlockOptions() {
            MaxDegreeOfParallelism = Environment.ProcessorCount,
            BoundedCapacity = 10
        });
    }

    static void LinkPipeline() {
        InputBuffer.LinkTo(transform, new DataflowLinkOptions() { PropagateCompletion = true });
        transform.LinkTo(action, new DataflowLinkOptions() { PropagateCompletion = true });
    }

    static MessageProcessed TransformMessage(MessageIn message) {
        return new MessageProcessed() {
            ProcessedValue = message.InputValue++,
            Completion = message.Completion
        };
    }

    static void CompletedProcessing(MessageProcessed message) {
        message.Completion.SetResult(message.ProcessedValue);
    }
} 
公共类控制器{
公共异步任务PostToPipeline(int inputValue){
var消息=新消息输入(inputValue);
MyPipeline.InputBuffer.Post(消息);
返回等待消息.Completion.Task;
}
}
公共类消息{
公共消息输入(int值){
输入值=值;
完成=新任务完成源();
}
公共int InputValue{get;set;}
公共任务完成源完成{get;set;}
}
已处理的公共类消息{
public int ProcessedValue{get;set;}
公共任务完成源完成{get;set;}
}
公共静态类MyPipeline{
公共静态缓冲块InputBuffer{get;private set;}
私有静态块变换;
私有静态动作块动作;
静态MyPipeline(){
BuildPipeline();
链接管道();
}
静态void BuildPipeline(){
InputBuffer=新的缓冲块();
transform=new TransformBlock((Func)TransformMessage,new ExecutionDataflowBlockOptions(){
MaxDegreeOfParallelism=Environment.ProcessorCount,
边界容量=10
});
action=新建ActionBlock((action)CompletedProcessing,新建ExecutionDataflowBlockOptions(){
MaxDegreeOfParallelism=Environment.ProcessorCount,
边界容量=10
});
}
静态无效链接管道(){
LinkTo(转换,新的DataflowLinkOptions(){PropagateCompletion=true});
LinkTo(action,newdataflowlinkoptions(){PropagateCompletion=true});
}
静态消息处理转换消息(消息中的消息){
返回新的MessageProcessed(){
ProcessedValue=message.InputValue++,
完成=消息。完成
};
}
静态void CompletedProcessing(MessageProcessed消息){
message.Completion.SetResult(message.ProcessedValue);
}
} 

有几种方法可以协调管道内特定工作的完成;等待完成源代码可能是满足您需求的最佳方法。

对于获取特定输入的管道输出,Dataflow没有很好的解决方案(因为它支持的不仅仅是简单的管道)

要解决这个问题,您可以创建一个
TaskCompletionSource
,并将其与输入一起发送到管道。管道中的每个块将其发送到下一个块,最后一个块调用其
SetResult()


然后,将输入发送到管道的代码可以等待
TaskCompletionSource
Task
等待管道的输出。

是的,我可以发送async/Post到块并在不等待完成的情况下返回,但当我真正想知道“返回的对象”时,这会导致设计问题。因此,理想情况下,我希望在不关闭所有管道的情况下实现异步完成,它必须永远等待收入,而无需重新初始化。太棒了!我的问题的漂亮解决方案。非常感谢你!