【问题标题】:C# Am i using lock correctly?C# 我是否正确使用了锁?
【发布时间】:2010-10-17 19:09:49
【问题描述】:

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

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 ();
        }
    }
}

【问题讨论】:

    标签: c# multithreading logging locking


    【解决方案1】:

    嗯,这对我来说看起来不错;我可能会实现IDisposable 作为Close() 文件的一种手段,但是...

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


    更新:

    一个想法:你可能想考虑如果文件已经存在会发生什么;你不想踩在你的日志上......

    【讨论】:

    • WriteStuff 是一个构造函数,为什么需要同步它?
    • @arul - 我疯了一分钟;编辑添加,编辑删除 - 关于 WriteStuff 的评论。精神错乱了。
    【解决方案2】:

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

    有几点值得注意(除了要非常小心死锁和严格测试以确保它们不会发生):

    • 最好在构造函数中对代码加锁,因为我相信在某些情况下可能可以在构造函数块完成执行之前调用方法。 (如果我在这个问题上错了,请有人纠正我。)
    • 本例中的StreamWriter 对象是私有的,这很好。如果它是受保护的或内部的,您当然必须谨慎对待其他代码如何使用该对象(事实上,我认为最好几乎总是将此类对象声明为私有)。
    • 您以正确的方式完成了锁定!锁定单独的私有实例对象总是最安全的,因为您知道该对象不能被除您自己的代码之外的任何其他代码锁定(如果您锁定 thisStreamWriter 对象本身)。

    不过,我可能遗漏了一些东西,并且上面未显示的其他代码可能会导致问题的可能性很小,但据我所知,除了 可能 构造函数代码周围缺少锁。当您开始执行更复杂的多线程时,尤其是跨类/实例时,您更有可能需要注意死锁情况。

    无论如何,希望对您有所帮助。

    【讨论】:

    • Noldorin,你在 8 年前发布了它,所以你可能还是发现了它,但是是的,虽然规范声明公共地址仅在构造新对象之后才从 IL newobj 返回,但 JIT 编译器可以在某些情况下会重新排序指令,因此锁定构造函数是一个很好的预防措施。
    【解决方案3】:

    事件处理程序与事件生成器位于同一线程上,这意味着您的应用程序最终可能会被您的日志文件写入阻塞。

    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 ();                
       }        
    }
    

    【讨论】:

    • 很抱歉投了反对票,但是:1)您刚刚放弃了 sender 参数。 2)通过产生另一个线程/使用线程池来解决(不存在的)“线程”问题是没有意义的。也许如果你能详细说明一下......
    • 1:无关紧要,因为 OP 没有使用它,并且无论如何都可以在需要时轻松通过。 2:除非应用程序中的每个 Foo 对象都在自己的线程上或在新线程上触发 SomeEvent ,否则日志记录将成为瓶颈。很奇怪,因为您不了解某些内容而投反对票...
    • 1) 我不认为 OP 的类会被称为 'WriteStuff' 并带有 'Foo_Update' 事件处理程序。 2)我相信 OP 正在寻找线程安全的记录器而不是异步记录器。苹果和橙子。
    • 1:嗯?这些只是 OP 使用的示例方法。 2: OP 似乎正在寻找将他的记录器写给我的建议。如果他还没有发现以同步方式记录到文件会阻止他的应用程序,他很快就会发现。
    • 其实我主要是想知道线程安全和设计错误。不过,感谢您指出日志记录时可能出现的阻塞问题。
    猜你喜欢
    • 2011-07-14
    • 2014-07-19
    • 2017-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-03
    相关资源
    最近更新 更多