C#FIFO队列就像没有等待的BLockingCollection

时间:2016-05-10 17:24:23

标签: c# multithreading queue

我想要一个具有以下要求的FIFO队列:

  • 如果queue为空,请等待添加一个元素
  • 只要一个元素在Q
  • 中,就开始处理
  • 如果Q中的待处理元素超过X,则删除它们。

我使用了这样的BlockingCollection

public LoggerReal() 
{
    main = (frmMain)Application.OpenForms[0];
    LogQueue = new BlockingCollection<logEntry>(GlobalSettings.LogQueueSize);
    Task.Run(() => {
        foreach (logEntry LE in LogQueue.GetConsumingEnumerable()) {    
            try {
                ProcessLogEntry(LE);
            } catch (Exception E) {
                functions.Logger.log("Error processing logEntry" + E.Message, "LOGPROCESSING", LOGLEVEL.ERROR);
                functions.printException(E);
            }

        }
        functions.Logger.log("Exiting Queue Task", "LOGPROCESSING", LOGLEVEL.ERROR);
    });
}

但是,我注意到日志似乎只在队列已满时显示。 ProcessLogEntry函数只是将它们放入ListBox

我尝试使用简单的队列而没有运气。

据我所知,ConcurrentQueue和其他Queue可能无法满足这些要求,或者我错了?我将队列处理器启动到一个任务中,这样它就可以永远等待,这不是问题,但只要数据可用就需要开始处理。

3 个答案:

答案 0 :(得分:0)

你的标题有点令人困惑(FIFO是一个队列,根据定义,阻塞集合会等待/阻塞?)。但是,我会猜到你想要的东西......

我假设你想要2个线程,一个正在添加到队列(编写器),另一个被阻止/等待处理项目一旦被添加(读者)

创建阻止集合:

var dataSink = new BlockingCollection<logEntry]>(new ConcurrentQueue<logEntry>());

作家&#39;线程只是添加并继续它的方式

dataSink.Add(logEntryToAdd);  // Add to collection and continue

读者&#39;线程阻塞,直到项目添加到队列

while (dataSink.Count > 0)
{
    ProcessLogEntry(dataSink.Take());
}

我不确定你的溢出&#34; X&#34;但也许在“添加”期间操作你可以得到计数,如果它超过&#39; x&#39;要么不要添加或出列第一项(取决于你的逻辑流程需要什么)。

显然,确保UI线程没有被阻止(UI线程不应该是&#39;阅读器&#39;,如果需要,创建第三个线程,阻止/从队列中读取,然后通过UI通知UI调用以更新列表框)否则您的UI将无法响应......

答案 1 :(得分:0)

如果我理解了您的要求,您可以使用常规Queue<T>基于Monitor的简单信令,如下所示:

<强>成员:

private readonly int maxSize;
private readonly Queue<logEntry> logQueue;
private bool stopRequest;

构造

maxSize = GlobalSettings.LogQueueSize;
logQueue = new Queue<logEntry>(maxSize);

制作人方法:

public void Add(logEntry logEntry)
{
    lock (logQueue)
    {
        if (stopRequest) return;
        logQueue.Enqueue(logEntry);
        if (logQueue.Count == 1)
            Monitor.Pulse(logQueue);
    }
}

停止流程工作者的方法:

public void Stop()
{
    lock (logQueue)
    {
        if (stopRequest) return;
        stopRequest = true;
        Monitor.Pulse(logQueue);
    }
}

流程工作者(使用Task.Run调用的方法):

private void ProcessWorker()
{
    while (true)
    {
        logEntry LE;
        lock (logQueue)
        {
            while (!stopRequest && logQueue.Count == 0)
                Monitor.Wait(logQueue);
            if (stopRequest) break;
            if (logQueue.Count > maxSize)
            {
                logQueue.Clear();
                continue;
            }
            LE = logQueue.Dequeue();
        }
        try
        {
            ProcessLogEntry(LE);
        }
        catch (Exception E)
        {
            functions.Logger.log("Error processing logEntry" + E.Message, "LOGPROCESSING", LOGLEVEL.ERROR);
            functions.printException(E);
        }
    }
    functions.Logger.log("Exiting Queue Task", "LOGPROCESSING", LOGLEVEL.ERROR);
}

这只是为了得到这个想法,您可以进一步调整实施以更好地满足您的需求。

答案 2 :(得分:0)

所以最后我使用了BlockingCollection,但我使用tryTake而不是使用ConsumingEnumerable进行while循环:

  Task.Run(() => {
            while (!LogQueue.IsCompleted) {

                logEntry LE;
                LogQueue.TryTake(out LE, Timeout.Infinite);

                try {
                    ProcessLogEntry(LE);
                } finally {
                   // Do nothing, because if logging cause issue, logging exception is likely to do so as well...
                }
            }
            //functions.Logger.log("Exiting Queue Task", "LOGPROCESSING", LOGLEVEL.ERROR); // Will not work if exiting Q
        });