我如何才能创建一个持续的处理过程;“流动”;在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
});