也许这个功能已经埋藏在.NET-Framework的某个地方,但我找不到它。 我需要一个接一个地按给定的顺序执行方法。这些方法应返回一些内容(例如对象),因此有一种方法可以对返回的数据作出反应(例如,由于发生错误,取消执行以下方法)。方法的执行应该在它自己的线程中运行,我应该能够随时向队列添加方法。 知道如何在c#中实现这个吗?
感谢Jon的评论,我试图自己实现这样一个队列。这可能是完全错误的 - 所以评论非常受欢迎; - )
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace FunctionQueue
{
public class ErrorResult
{
public string ErrorMessage { get; set; }
public bool CancelQueue { get; set; }
}
public class FunctionQueue
{
private readonly ConcurrentQueue<Func<object>> Queue;
private bool isExecuting;
private bool isCanceled;
private readonly Action<ErrorResult> onError;
private readonly Action onComplete;
public FunctionQueue(Action<ErrorResult> onError = null, Action onComplete = null)
{
this.Queue = new ConcurrentQueue<Func<object>>();
this.onError = onError;
this.onComplete = onComplete;
}
public void AddFunctionToQueue( Func<object> functionToAdd, bool startQueue = false )
{
this.Queue.Enqueue(functionToAdd);
if (startQueue && !this.isExecuting) this.ProcessQueue();
}
public void ProcessQueue()
{
if( this.Queue.Count > 0 )
{
Task.Run( () =>
{
this.isCanceled = false;
this.isExecuting = true;
Func<object> functionToExecuteNext;
while( !isCanceled && this.Queue.TryDequeue( out functionToExecuteNext ) )
{
object result = functionToExecuteNext();
ErrorResult errorResult = result as ErrorResult;
if( errorResult != null )
{
if( this.onError != null ) this.onError( errorResult );
if( errorResult.CancelQueue ) this.isCanceled = true;
}
}
} );
}
this.isExecuting = false;
if( this.onComplete != null ) this.onComplete();
}
}
}
我想添加另一个功能,但不幸的是我不知道如何实现这个:我想为每个添加的功能添加一个可选的回调。当给定的函数完成时,应该调用该回调。如何添加此功能?
答案 0 :(得分:4)
听起来你可以使用Func<object>
的生产者/消费者队列。
假设您使用的是.NET 4,则应使用适当BlockingCollection<T>
周围的IProducerConsumerCollection<T>
包装器(例如ConcurrentQueue<T>
)。这些类型旨在帮助简化生产者/消费者的情况。
如果你需要建立一个管道,你还应该看看Dataflow,它提供了一些更高层次的构造。
答案 1 :(得分:0)
您不应该使用ConcurrentQueue<T>
。使用BlockingCollection<T>
,它可以提供更好的包装,可以使用。
然后您可以使用以下方式处理队列:
public void ProcessQueue()
{
Func<object> functionToExecuteNext;
while (!IsCancelled && queue.TryTake(out functionToExecuteNext, Timeout.Infinite))
{
// execute the function
}
}
在你的程序初始化中:
queue = new BlockingCollection<Func<object>>();
var t = new Task(ProcessQueue, TaskCreationOptions.LongRunning);
t.Start();
// program does stuff
// time to shut down
queue.CompleteAdding(); // mark the queue as complete for adding
// at this point you'll want to wait until the queue is empty.
// unless you want to quit immediately. Then just set the IsCancelled flag.
此处,TryTake
在队列上执行非忙等待。它将等待一个项目可用,或者直到队列被标记为完成。关键是它在等待时不消耗CPU资源。
当生产者完成向队列添加内容时,它会调用queue.CompleteAdding
,当队列为空时,TryTake
将false
退出。
所以你可以在程序开始时启动队列处理线程,它会在项目进入时处理它们。不需要isExecuting
标志或AddFunctionToQueue
方法检查标志并启动线程。
我使用Task
构造函数创建具有LongRunning
选项的任务,以便调度程序可以更好地安排其他工作。