如何有效地异步登录? [英] How to effectively log asynchronously?

查看:27
本文介绍了如何有效地异步登录?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的一个项目中使用 Enterprise Library 4 进行日志记录(和其他目的).我注意到我正在执行的日志记录会产生一些成本,我可以通过在单独的线程上进行日志记录来减轻这些成本.

I am using Enterprise Library 4 on one of my projects for logging (and other purposes). I've noticed that there is some cost to the logging that I am doing that I can mitigate by doing the logging on a separate thread.

我现在这样做的方法是创建一个 LogEntry 对象,然后在调用 Logger.Write 的委托上调用 BeginInvoke.

The way I am doing this now is that I create a LogEntry object and then I call BeginInvoke on a delegate that calls Logger.Write.

new Action<LogEntry>(Logger.Write).BeginInvoke(le, null, null);

我真正想做的是将日志消息添加到队列中,然后让一个线程将 LogEntry 实例从队列中拉出并执行日志操作.这样做的好处是日志记录不会干扰正在执行的操作,并且并非每个日志记录操作都会导致作业被抛出到线程池中.

What I'd really like to do is add the log message to a queue and then have a single thread pulling LogEntry instances off the queue and performing the log operation. The benefit of this would be that logging is not interfering with the executing operation and not every logging operation results in a job getting thrown on the thread pool.

如何以线程安全的方式创建一个支持多个写入器和一个读取器的共享队列?一些旨在支持多个写入器(不会导致同步/阻塞)和单个读取器的队列实现示例将不胜感激.

How can I create a shared queue that supports many writers and one reader in a thread safe way? Some examples of a queue implementation that is designed to support many writers (without causing synchronization/blocking) and a single reader would be really appreciated.

关于替代方法的建议也将不胜感激,不过我对更改日志框架不感兴趣.

Recommendation regarding alternative approaches would also be appreciated, I am not interested in changing logging frameworks though.

推荐答案

我前段时间写了这段代码,可以随意使用.

I wrote this code a while back, feel free to use it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace MediaBrowser.Library.Logging {
    public abstract class ThreadedLogger : LoggerBase {

        Queue<Action> queue = new Queue<Action>();
        AutoResetEvent hasNewItems = new AutoResetEvent(false);
        volatile bool waiting = false;

        public ThreadedLogger() : base() {
            Thread loggingThread = new Thread(new ThreadStart(ProcessQueue));
            loggingThread.IsBackground = true;
            loggingThread.Start();
        }


        void ProcessQueue() {
            while (true) {
                waiting = true;
                hasNewItems.WaitOne(10000,true);
                waiting = false;

                Queue<Action> queueCopy;
                lock (queue) {
                    queueCopy = new Queue<Action>(queue);
                    queue.Clear();
                }

                foreach (var log in queueCopy) {
                    log();
                }
            }
        }

        public override void LogMessage(LogRow row) {
            lock (queue) {
                queue.Enqueue(() => AsyncLogMessage(row));
            }
            hasNewItems.Set();
        }

        protected abstract void AsyncLogMessage(LogRow row);


        public override void Flush() {
            while (!waiting) {
                Thread.Sleep(1);
            }
        }
    }
}

一些优点:

  • 它使后台记录器保持活动状态,因此不需要启动和关闭线程.
  • 它使用单个线程为队列提供服务,这意味着永远不会出现 100 个线程为队列提供服务的情况.
  • 它复制队列以确保在执行日志操作时队列不被阻塞
  • 它使用 AutoResetEvent 来确保 bg 线程处于等待状态
  • 恕我直言,很容易理解

这是一个稍微改进的版本,请记住我对它进行了很少的测试,但它确实解决了一些小问题.

Here is a slightly improved version, keep in mind I performed very little testing on it, but it does address a few minor issues.

public abstract class ThreadedLogger : IDisposable {

    Queue<Action> queue = new Queue<Action>();
    ManualResetEvent hasNewItems = new ManualResetEvent(false);
    ManualResetEvent terminate = new ManualResetEvent(false);
    ManualResetEvent waiting = new ManualResetEvent(false);

    Thread loggingThread; 

    public ThreadedLogger() {
        loggingThread = new Thread(new ThreadStart(ProcessQueue));
        loggingThread.IsBackground = true;
        // this is performed from a bg thread, to ensure the queue is serviced from a single thread
        loggingThread.Start();
    }


    void ProcessQueue() {
        while (true) {
            waiting.Set();
            int i = ManualResetEvent.WaitAny(new WaitHandle[] { hasNewItems, terminate });
            // terminate was signaled 
            if (i == 1) return; 
            hasNewItems.Reset();
            waiting.Reset();

            Queue<Action> queueCopy;
            lock (queue) {
                queueCopy = new Queue<Action>(queue);
                queue.Clear();
            }

            foreach (var log in queueCopy) {
                log();
            }    
        }
    }

    public void LogMessage(LogRow row) {
        lock (queue) {
            queue.Enqueue(() => AsyncLogMessage(row));
        }
        hasNewItems.Set();
    }

    protected abstract void AsyncLogMessage(LogRow row);


    public void Flush() {
        waiting.WaitOne();
    }


    public void Dispose() {
        terminate.Set();
        loggingThread.Join();
    }
}

相比原版的优势:

  • 它是一次性的,所以你可以摆脱异步记录器
  • 改进了刷新语义
  • 它对突然响起然后是沉默的反应会稍微好一些

这篇关于如何有效地异步登录?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆