我如何才能创建一个持续的处理过程;“流动”;在C#4中使用第三方物流

我如何才能创建一个持续的处理过程;“流动”;在C#4中使用第三方物流,c#,c#-4.0,queue,message-queue,task-parallel-library,C#,C# 4.0,Queue,Message Queue,Task Parallel Library,我不确定是否可以执行以下操作,但我希望以并行、限制的方式调用一些操作,但要保持处理流程的连续性,而不返回到使用计时器或循环/睡眠周期 到目前为止,我已经让它工作,它加载了来自某个来源的大量输入。。。然后以受控方式并行处理它们&循环如下 static void Main(string[] args) { while(true) //Simulate a Timer Elapsing... { IEnumerable<int> inputs = new L

我不确定是否可以执行以下操作,但我希望以并行、限制的方式调用一些操作,但要保持处理流程的连续性,而不返回到使用计时器或循环/睡眠周期

到目前为止,我已经让它工作,它加载了来自某个来源的大量输入。。。然后以受控方式并行处理它们&循环如下

static void Main(string[] args)
{
    while(true) //Simulate a Timer Elapsing...
    {
        IEnumerable<int> inputs = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};  
        //Simulate querying database queue tables for next batch of entries

        RunAllActions(inputs, 3); //Max 3 at a time.
    }
}

static void RunAllActions(IEnumerable<int> inputs, int maxConcurrency)
{
    var options = new ParallelOptions() {MaxDegreeOfParallelism = maxConcurrency};

    Parallel.ForEach<int>(inputs, options, DoWork);
    //Blocks here until all inputs are processed.
    Console.WriteLine("Batch of Work Done!!!");
}

static void DoWork(int input)
{
    Console.WriteLine("Starting Task {0}", input);
    System.Threading.Thread.Sleep(3000);
    Console.WriteLine("Finishing Task {0}", input);
}
static void Main(字符串[]args)
{
while(true)//模拟计时器正在流逝。。。
{
IEnumerable inputs=new List(){1,2,3,4,5,6,7,8,9,10};
//模拟为下一批条目查询数据库队列表
RunAllActions(输入,3);//一次最多3个。
}
}
静态void RunAllActions(IEnumerable输入,int-maxConcurrency)
{
var options=new ParallelOptions(){MaxDegreeOfParallelism=maxConcurrency};
Parallel.ForEach(输入、选项、工作);
//在此处阻塞,直到处理完所有输入。
Console.WriteLine(“完成了一批工作!!!”;
}
静态无效工作(整数输入)
{
WriteLine(“启动任务{0}”,输入);
系统线程线程睡眠(3000);
WriteLine(“正在完成任务{0}”,输入);
}
我想知道的是,TPL中是否有一种结构可以让它一直运行。。。这样,我就可以用MessageQueue Received事件替换“计时器超时”和“数据库轮询”

以下是我想要实现的粗略版本。。。我还有其他方法可以做到这一点,但我想知道第三方物流中是否有这种模式

internal class Engine
{
    private MessageQueue mq;
    private Queue<int> myInternalApplicationQueue;

    public Engine()
    {
        //Message Queue to get new task inputs from
        mq = new MessageQueue();
        mq.ReceiveCompleted += new ReceiveCompletedEventHandler(mq_ReceiveCompleted);

        // internal Queue to put them in.
        myInternalApplicationQueue = new Queue<int>();
    }

    void mq_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
    {
        //On MQ Receive, pop the input in a queue in my app
        int input = (int) e.Message.Body;

        myInternalApplicationQueue.Enqueue(input);
    }

    public void StartWorking()
    {
        //Once this gets called, it doesn't stop... it just keeps processing/watching that queue
        //processing the tasks as fast as it's allowed while the app is running.
        var options = new ParallelOptions() { MaxDegreeOfParallelism = 3 };
        Parallel.KeepWorkingOnQueue<int>(myInternalApplicationQueue, options, DoWork);
        //       ^^^^^^^^^^^^^^^^^^ <----- THIS GUY
    }

}
内部类引擎
{
专用消息队列mq;
私有队列myInternalApplicationQueue;
公共引擎()
{
//从中获取新任务输入的消息队列
mq=新消息队列();
mq.ReceiveCompleted+=新的ReceiveCompletedEventHandler(mq_ReceiveCompleted);
//将它们放入的内部队列。
myInternalApplicationQueue=新队列();
}
void mq_ReceiveCompleted(对象发送方,ReceiveCompletedEventArgs e)
{
//在MQ接收时,将输入弹出到我的应用程序的队列中
int输入=(int)e.Message.Body;
myInternalApplicationQueue.Enqueue(输入);
}
公共无效StartWorking()
{
//一旦调用它,它就不会停止…它只是继续处理/监视该队列
//在应用程序运行时,尽可能快地处理任务。
var options=new ParallelOptions(){MaxDegreeOfParallelism=3};
Parallel.KeepWorkingQueue(myInternalApplicationQueue、options、DoWork);
//^^^^^^^^^^^^^^^^^您可以使用来处理这种类型的操作,这实际上是生产者/消费者场景

基本上,您将设置一个
BlockingCollection
并将其用作“生产者”。然后您将有三个(或任意数量的)消费者任务(通常设置为长时间运行的任务)来处理元素(通过在标准foreach循环中调用
BlockingCollection.getConsumineumerable()

然后根据需要将项目添加到集合中,它们将不断被处理。完成后,您将调用,这将导致foreach循环完成,整个过程将停止


作为旁注-您通常不希望在
getconsumineGenumerable()上使用
Parallel.ForEach
来自
BlockingCollection
-除非您自己处理分区,否则至少不会。通常最好使用多个任务,并让每个任务按顺序迭代。原因是
Parallel.ForEach
中的默认分区方案会导致问题(它会等到“块”出现)大量数据可用,而不是立即处理项目,“块”随着时间的推移变得越来越大。

正如里德指出的,BlockingCollection是一种很好的“手动”方式。缺点是你还必须自己管理消费者

对于这样的场景,您可能需要研究的另一种方法是研究,这种方法需要大量的协调工作。特别是在这样的场景中,您可以使用,当消息从队列中传入时,您只需将新的数据块发送到
ActionBlock
,它将自动在封面下使用TPL worker线程对其进行逻辑处理。这将使您的
引擎
类看起来有点像这样:

ActionBlock<int> myActionBlock = new ActionBlock<int>(this.ProcessWorkItem);

void mq_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)      
{      
    int input = (int)e.Message.Body;

    // Post the data to the action block
    this.myActionBlock.Post(input);
}

private void ProcessWorkItem(int workItemData)
{
    // ActionBlock will hand each work item to this method for processing
}

谢谢里德。我会尝试一下。谢谢德鲁。这看起来真的很有趣。不幸的是,我现在被锁定在.NET 4或更低版本。虽然TDF被嵌入到.NET 4.5中,但有一个单独的TDF下载,它位于.NET 4 TPL之上。这可以从我在回答中链接的TPL数据流主页上获得,但这里是直接下载链接()这是自述文件()干杯,德鲁。是的,我已经在我自己的测试虚拟机上下载了。但是我正在做的工作将被交付到现有的生产环境中,这就是我所说的锁定到.NET 4.0的意思。再次感谢。@EoinCampbell啊,我明白了。太糟糕了,因为它让生活变得非常简单。听起来你只需要去看“手册”就行了那就使用BlockingCollection吧。今天我在DB队列和后台处理器之间尝试了一些示例代码。如果你有时间看一下的话,我刚刚放了一个简化版。我有点担心我长期使用BlockingCollection。
ActionBlock<int> myActionBlock = new ActionBlock<int>(
                                     this.ProcessWorkItem, 
                                     new ExecutionDataflowBlockOptions
                                     {
                                         MaxDegreeOfParallelism = 4,
                                         BoundedCapacity = 100
                                     });