gpt4 book ai didi

c# - Monitor.PulseAll 的正确用法?

转载 作者:行者123 更新时间:2023-11-30 15:41:27 28 4
gpt4 key购买 nike

我正在为我们正在运行的 ASP.NET 应用程序编写我们自己的记录器。在当前的代码库中,我们似乎遇到了线程问题,我正在尝试逐个组件地消除以找出导致此问题的原因。症状是应用程序启动后网络服务器上的 CPU 达到 100%。

我要删除的第一件事是我编写的记录器,因为它是代码库中最新添加的内容之一。记录器背后的想法如下:

  • 1 个名为 LogManager 的静态类,用于跟踪所有 ILogger 实现
  • 1 个名为 LogSettings 的静态类,用于跟踪配置文件中的所有设置。
  • 1 个具有 MessageQueue 的 XmlLogger,用于保存消息并将它们写入 xml 文件。

可以从任何地方调用静态类,并且 XmlLogger 有一个后台线程从 MessageQueue 获取消息并处理它。所以我需要使对该队列的访问是线程安全的。 MessageQueue 类的代码如下所示:

public sealed class MessageQueue
{
#region Private Fields

private readonly Queue<IMessage> _messageQueue;

#endregion

#region Constructor

/// <summary>
/// <para>Creates a new instance of the class and initializes all internal fields.</para>
/// </summary>
public MessageQueue()
{
_messageQueue = new Queue<IMessage>();
}

#endregion

#region Properties

/// <summary>
/// <para>Gets the number of <see cref="IMessage"/> objects in the queue.</para>
/// </summary>
public int NumberOfMessage
{
get
{
lock(_messageQueue)
{
return _messageQueue.Count;
}
}
}

#endregion

#region Public Members

/// <summary>
/// <para>Adds a new <see cref="IMessage"/> to the bottom of the queue.</para>
/// </summary>
/// <param name="message">The <see cref="IMessage"/> to be added to the queue.</param>
public void AddMessage(IMessage message)
{
lock(_messageQueue)
{
_messageQueue.Enqueue(message);
Monitor.PulseAll(_messageQueue);
}
}

/// <summary>
/// <para>Gets the first <see cref="IMessage"/> from the queue.</para>
/// </summary>
/// <returns>The first <see cref="IMessage"/> from the queue.</returns>
public IMessage GetMessage()
{
lock(_messageQueue)
{
while (_messageQueue.Count == 0) Monitor.Wait(_messageQueue);
return _messageQueue.Dequeue();
}
}

#endregion
}

向任何记录器的 MessageQueue 添加消息的代码如下所示:

/// <summary>
/// <para>Logs a specified <see cref="IMessage"/> in the various loggers.</para>
/// </summary>
/// <param name="message">The <see cref="IMessage"/> to be logged.</param>
public void LogMessage(IMessage message)
{
lock(_loggerLock)
{
foreach (AbstractLogger logger in _loggers)
logger.MessageQueue.AddMessage(message);
}
}

我已经锁定了 LogManager,因为消息的插件不应与从队列中添加/删除记录器冲突。我确定这里不存在问题。下面的代码显示了包含 MessageQueue 对象的 XmlLogger 的处理例程。

private void ProcessMessage()
{
// This thread should be running indefinitly untill the abort is called.
while (true)
{
try
{
// Get the first message available from the Queue
IMessage message = MessageQueue.GetMessage();

// If we could retrieve a valid message, process it.
if (message != null && (LogSettings.Level & message.Level) == message.Level)
{
// Determine the path to the logfile
string logpath = DetermineFilePath(message.Context);

// Write the message into the file.
WriteMessage(logpath, message);
}
}
catch(ThreadAbortException)
{
break;
}
catch (Exception ex)
{
Debug.WriteLine(string.Format("InnerException caught: '{0}'", ex.Message));
}
finally
{
Thread.Sleep(100);
}
}
}

我想知道的是,这段代码是使用 Monitor.Pulse 和 Monitor.Wait 例程的正确方法吗?如果不是,我应该更改什么以防止出现问题?

编辑:请不要就使用现有记录器提出问题/讨论。这不是一个选择

EDIT2:从 anwser 运行该工具,这是输出:

------------------------------------ 5956 Kernel: 0 User: 156250 TenForce.Execution.Logging.Loggers.XmlLogger.ProcessMessage System.Threading.ExecutionContext.Run System.Threading.ExecutionContext.Run System.Threading.ThreadHelper.ThreadStart Other Stacks:

TenForce.Execution.Logging.Loggers.XmlLogger.ProcessMessage System.Threading.ExecutionContext.Run System.Threading.ExecutionContext.Run System.Threading.ThreadHelper.ThreadStart

对我来说听起来有点太高了......

最佳答案

是的,看起来不错;您的出队正确地进行了检查/循环(并且没有假设脉冲意味着现在有数据,这可能会导致异常),所以大部分时间应该是空闲的。你的入队是相似的。也许我会补充:如果您添加了第一项,则仅脉冲:

        lock(_messageQueue)
{
_messageQueue.Enqueue(message);
if(_messageQueue.Count == 1) Monitor.PulseAll(_messageQueue);
}

(否则,您的读者不会等待)

要调查高 CPU,请使用 CPU 分析器 - 猜测通常不是一个好的方法。山姆藏红花 has one here我们使用的;它可以在生产网络服务器上使用(只是......不是所有的时间!)

关于c# - Monitor.PulseAll 的正确用法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8307318/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com