Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/291.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#我是否正确使用了锁?_C#_Multithreading_Logging_Locking - Fatal编程技术网

C#我是否正确使用了锁?

C#我是否正确使用了锁?,c#,multithreading,logging,locking,C#,Multithreading,Logging,Locking,我目前正在尝试编写一个线程安全的记录器类。我不太熟悉这方面的正确设计和最佳实践。我的代码有缺陷吗 public class WriteStuff { private readonly StreamWriter m_Writer; private readonly object m_WriteLock = new object (); public WriteStuff(String path) { m_Writer = File.CreateTe

我目前正在尝试编写一个线程安全的记录器类。我不太熟悉这方面的正确设计和最佳实践。我的代码有缺陷吗

public class WriteStuff
{
    private readonly StreamWriter m_Writer;
    private readonly object m_WriteLock = new object ();

    public WriteStuff(String path)
    {
        m_Writer = File.CreateText (path);

        m_Writer.WriteLine ("x");
        m_Writer.Flush ();
    }

    public void ListenTo(Foo foo)
    {
        foo.SomeEvent += new EventHandler<SomeArgs> (Foo_Update);
    }

    private void Foo_Update(object sender, SomeArgs args)
    {
        lock (m_WriteLock) {
            m_Writer.WriteLine (args);
            m_Writer.Flush ();
        }
    }
}
公共类WriteStuff
{
私有只读StreamWriter m_Writer;
私有只读对象m_WriteLock=新对象();
公共WriteStuff(字符串路径)
{
m_Writer=File.CreateText(路径);
m_Writer.WriteLine(“x”);
m_Writer.Flush();
}
公共无效列表(Foo-Foo)
{
foo.SomeEvent+=新的事件处理程序(foo_更新);
}
私有void Foo_更新(对象发送方,SomeArgs args)
{
锁(m_WriteLock){
m_Writer.WriteLine(args);
m_Writer.Flush();
}
}
}

好吧,我觉得这没问题;我可能会实现
IDisposable
作为关闭()文件的一种方法,但是

当然,您也可以使用任何(许多)预先封装的日志框架


更新:


一个想法:你可能想考虑如果文件已经存在会发生什么;您不想践踏日志…

事件处理程序与事件生成器位于同一线程上,这意味着您的应用程序可能会因日志文件写入而中断

private void Foo_Update(object sender, SomeArgs args)        { 
    ThreadPool.QueueUserWorkItem(WriteAsync, args);
}

private void WriteAsync(object state) {  
    SomeArgs args = (SomeArgs)state;    
    lock (m_WriteLock) {                        
       m_Writer.WriteLine (args);                        
       m_Writer.Flush ();                
   }        
}

从多线程的角度来看,您发布的内容看起来不错。虽然我可能是错的,但似乎任何其他执行多线程(甚至使用foo对象)的代码都应该是安全的。当然,我在代码的这一部分中看不到任何内容

无论如何,有几件事值得注意(除了非常小心死锁和严格测试以确保不会发生死锁外):

  • 最好在构造函数中的代码周围加上一个锁,因为我相信在某些情况下,可以在构造函数块完成执行之前调用方法。(如果我在这一点上错了,请有人纠正我。)
  • 本例中的
    StreamWriter
    对象是私有的,这很好。如果它是受保护的或内部的,那么您肯定必须小心其他代码如何使用该对象(事实上,我认为最好几乎总是将此类对象声明为私有)
  • 你锁对了!锁定一个单独的私有实例对象总是最安全的,因为您知道该对象不能被您自己的代码以外的任何其他代码锁定(如果您锁定
    this
    StreamWriter
    对象本身,则情况并非如此)
尽管如此,我可能遗漏了一些东西,上面没有显示的其他代码也有可能会导致问题,但就我所见,代码没有缺陷,除了构造函数代码周围可能缺少锁。当您开始执行更复杂的多线程时,尤其是跨类/实例执行多线程时,您可能更需要注意死锁情况


不管怎样,希望这能有所帮助。

WriteStuff是一个构造函数,为什么需要同步它?@arul-我疯了一分钟;编辑以添加,编辑以删除-关于WriteStuff的注释。疯狂结束。很抱歉投了反对票,但是:1)你刚刚放弃了发件人的论点。2) 通过生成另一个线程/使用线程池来解决(不存在的)“线程”问题毫无意义。如果你能详细说明一下的话……1:是不相关的,因为OP没有利用它,而且如果需要的话,它可以很容易地传递给别人。2:除非应用程序中的每个Foo对象都在自己的线程上,或者在新线程上触发某个事件,否则日志记录将成为瓶颈。因为您不了解某些事情,所以拒绝投票是很奇怪的……1)我也不认为OP的类会被称为带有“Foo_Update”事件处理程序的“WriteStuff”。2) 我相信OP正在寻找线程安全记录器,而不是异步记录器。苹果和桔子。1:嗯?这些只是OP使用的示例方法。他说:OP似乎在寻求建议,给我写他的日志。如果他还没有发现以同步方式登录文件会阻止他的应用程序,他很快就会发现。实际上,我主要想知道线程安全和设计错误。感谢您指出了日志记录时可能出现的阻塞问题。Noldorin,您在8年前发布了它,因此您可能发现了它,但是是的,虽然规范规定只有在构建新对象后才能从IL newobj返回公共地址,但JIT编译器在某些情况下可以重新排序指令,所以,锁定构造函数是一个很好的预防措施。