如何使类线程安全

Gha*_*han 22 c# multithreading thread-safety

我正在写一个C#应用程序.我有(一种)日志记录类.并且许多线程将使用此日志记录类.如何使这个类线程安全?我应该把它作为单身人士吗?有什么最好的做法?有没有关于如何使其成为线程安全的文档?

谢谢

jer*_*lan 19

在C#中,任何对象都可用于保护"临界区",换句话说,代码不能同时由两个线程执行.

例如,以下内容将同步对SharedLogger.Write方法的访问,因此在任何给定时间只有一个线程记录消息.

public class SharedLogger : ILogger
{
   public static SharedLogger Instance = new SharedLogger();

   public void Write(string s)
   {
      lock (_lock)
      {
         _writer.Write(s);
      }
   }

   private SharedLogger() 
   { 
      _writer = new LogWriter();
   }

   private object _lock;
   private LogWriter _writer;
}
Run Code Online (Sandbox Code Playgroud)

  • 我同意使用现有的记录器实现,但如果他正在处理多线程代码,他最终将需要知道如何正确地同步对各种资源的访问,并且这个相同的过程可以应用于其他地方...... (4认同)

Mat*_*vis 11

我不确定我是否可以添加任何有关使日志记录类线程安全的内容.如前所述,为此,您必须同步对资源的访问,即日志文件,以便一次只有一个线程尝试登录它.C#lock关键字是执行此操作的正确方法.

但是,我将讨论(1)单例方法和(2)最终决定使用的方法的可用性.

(1)如果您的应用程序将其所有日志消息写入单个日志文件,那么单例模式肯定是要走的路.日志文件将在启动时打开并在关闭时关闭,单例​​模式完全符合这种操作概念.正如@dtb指出的那样,请记住,将类设为单例并不能保证线程安全.使用lock关键字.

(2)关于方法的可用性,请考虑以下建议的解决方案:

public class SharedLogger : ILogger
{
   public static SharedLogger Instance = new SharedLogger();
   public void Write(string s)
   {
      lock (_lock)
      {
         _writer.Write(s);
      }
   }
   private SharedLogger()
   {
       _writer = new LogWriter();
   }
   private object _lock;
   private LogWriter _writer;
}
Run Code Online (Sandbox Code Playgroud)

我先说这种方法通常没问题.它SharedLogger通过Instance静态变量定义单例实例,并防止其他人通过私有构造函数实例化该类.这是单身人士模式的本质,但我强烈建议走得太远之前阅读并遵循Jon Skeet关于C#中单身人士的建议.

但是,我想要关注的是这个解决方案的可用性.通过'可用性',我指的是使用此实现来记录消息的方式.考虑调用的样子:

SharedLogger.Instance.Write("log message");
Run Code Online (Sandbox Code Playgroud)

整个'Instance'部分只是看起来不对,但考虑到实现,没有办法避免它.相反,考虑这个替代方案:

public static class SharedLogger : ILogger
{
   private static LogWriter _writer = new LogWriter();
   private static object _lock = new object();
   public static void Write(string s)
   {
       lock (_lock)
       {
           _writer.Write(s);
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

请注意,该类现在是静态的,这意味着它的所有成员和方法都必须是静态的.它与前面的示例没有实质性的不同,但请考虑其用法.

SharedLogger.Write("log message");
Run Code Online (Sandbox Code Playgroud)

这对代码来说简单得多.

重点不是诋毁前一种解决方案,而是建议您选择的任何解决方案的可用性是一个不容忽视的重要方面.一个好的,可用的API可以使代码编写更简单,更优雅,更易于维护.


cap*_*aig 8

我会使用现成的记录器,因为有几个坚如磐石且易于使用.无需自己动手.我推荐Log4Net.

  • 在记录方面我同意这一点.但它没有回答这个问题. (10认同)

BCS*_*BCS 6

  • 尝试使用局部变量进行大多数计算,然后在一个快速lock编辑块中更改对象的状态.
  • 请记住,某些变量可能会在您阅读它们和更改状态之间发生变化.