在自己的线程中调用排队方法

时间:2013-05-13 08:11:32

标签: c# .net queue

也许这个功能已经埋藏在.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();
        }
    }
}

我想添加另一个功能,但不幸的是我不知道如何实现这个:我想为每个添加的功能添加一个可选的回调。当给定的函数完成时,应该调用该回调。如何添加此功能?

2 个答案:

答案 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,当队列为空时,TryTakefalse退出。

所以你可以在程序开始时启动队列处理线程,它会在项目进入时处理它们。不需要isExecuting标志或AddFunctionToQueue方法检查标志并启动线程。

我使用Task构造函数创建具有LongRunning选项的任务,以便调度程序可以更好地安排其他工作。