ManualResetEvent
ManualResetEvent 类似于AutoResetEvent,不过要手动设置Reset;并且ManualResetEvent可以控制多个线程协调。
ManualResetEventSlim 是轻型级的类,可以替代ManualResetEvent 使用。
Wait()内部轮询检测。判断IsSet属性,如果为false,线程会挂起;如果为true,线程不会挂起,会继续执行。
Reset()重置信号,IsSet=false。
Set() 设置信号,IsSet=true。
ManualResetEventSlim实现日志,通知线程在队列中有新的内容,当队列中写完日志后,重置信号,等待下次日志信息。
using log4net;
using log4net.Config;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
/* 优点:适用高并发场景,高性能写日志
缺点:程序崩溃或电脑断电,队列中的日志可能未写完而丢失
*/
public class LogHelper
{
// 延迟加载
private static readonly Lazy<LogHelper> _lazy = new Lazy<LogHelper>(() => new LogHelper());
/// <summary>
/// 单例
/// </summary>
public static LogHelper Instance() => _lazy.Value;
private LogHelper()
{
var configFile = "log4net.config";
var fi = new FileInfo(configFile);
if (!fi.Exists)
{
throw new Exception("log4net.config is NULL!");
}
XmlConfigurator.Configure(fi);
_log = LogManager.GetLogger(typeof(LogHelper));
_queue = new ConcurrentQueue<LogMessage>();
// 创建无信号状态
_mre = new ManualResetEventSlim(false);
}
/// <summary>
/// 日志
/// </summary>
private readonly ILog _log;
/// <summary>
/// 信号
/// </summary>
private readonly ManualResetEventSlim _mre;
/// <summary>
/// 队列
/// </summary>
private readonly ConcurrentQueue<LogMessage> _queue;
// 线程记录日志,只在程序初始化时调用一次
public void Register()
{
Task.Run(WriteLogAsync);
}
private async Task WriteLogAsync()
{
while (true)
{
// 等待。收到被设置信号true,不再阻塞,继续执行后续代码
_mre.Wait();
// 从队列写入磁盘
LogMessage msg;
while (_queue.Count > 0 && _queue.TryDequeue(out msg))
{
switch (msg.Level)
{
case LogLevel.Debug:
_log.Debug(msg.Message, msg.Exception);
break;
case LogLevel.Info:
_log.Info(msg.Message, msg.Exception);
break;
case LogLevel.Warn:
_log.Warn(msg.Message, msg.Exception);
break;
case LogLevel.Error:
_log.Error(msg.Message, msg.Exception);
break;
case LogLevel.Fatal:
_log.Fatal(msg.Message, msg.Exception);
break;
default:
break;
}
}
// 必须手动重置信号false
_mre.Reset();
await Task.Delay(1);
}
}
public void EnqueueMessage(string message, LogLevel level, Exception ex = null)
{
if ((level == LogLevel.Debug && _log.IsDebugEnabled) ||
(level == LogLevel.Info && _log.IsInfoEnabled) ||
(level == LogLevel.Warn && _log.IsWarnEnabled) ||
(level == LogLevel.Error && _log.IsErrorEnabled) ||
(level == LogLevel.Fatal && _log.IsFatalEnabled))
{
_queue.Enqueue(new LogMessage
{
Message = message,
Level = level,
Exception = ex,
});
// 设置信号true。通知线程
_mre.Set();
}
}
public static void Debug(string msg, Exception ex = null)
{
Instance().EnqueueMessage(msg, LogLevel.Debug, ex);
}
public static void Info(string msg, Exception ex = null)
{
Instance().EnqueueMessage(msg, LogLevel.Info, ex);
}
public static void Warn(string msg, Exception ex = null)
{
Instance().EnqueueMessage(msg, LogLevel.Warn, ex);
}
public static void Error(string msg, Exception ex = null)
{
Instance().EnqueueMessage(msg, LogLevel.Error, ex);
}
public static void Fatal(string msg, Exception ex = null)
{
Instance().EnqueueMessage(msg, LogLevel.Fatal, ex);
}
}
/// <summary>
/// 日志内容
/// </summary>
public class LogMessage
{
public string Message { get; set; }
public LogLevel Level { get; set; }
public Exception Exception { get; set; }
}
/// <summary>
/// 日志等级
/// </summary>
public enum LogLevel
{
Debug,
Info,
Warn,
Error,
Fatal,
}
参考链接:同步基元概述