【问题标题】:lock keyword in C#C#中的lock关键字
【发布时间】:2013-03-21 18:00:13
【问题描述】:

我从 MSDN 了解 lock 关键字的主要功能

lock 语句(C# 参考)

lock 关键字标记一个语句 块作为关键部分 获得互斥锁 对于给定的对象,执行 声明,然后释放 锁。

什么时候应该使用锁?

例如,它对多线程应用程序很有意义,因为它可以保护数据。但是当应用程序不分离任何其他线程时是否有必要?

使用锁是否存在性能问题?

我刚刚继承了一个在所有地方都使用锁的应用程序,它是单线程的,我想知道我应该把它们留在里面吗,它们是否有必要?

请注意,这更像是一个常识性问题,应用程序速度很好,我想知道这是否是未来要遵循的良好设计模式,或者除非绝对需要,否则应该避免这种情况。

【问题讨论】:

    标签: c# multithreading design-patterns locking


    【解决方案1】:

    什么时候应该使用锁?

    应该使用锁来保护多线程代码中的共享资源。不为别的。

    但是当应用程序不分离任何其他线程时是否有必要?

    绝对不是。这只是浪费时间。但是请确保您没有隐式使用系统线程。例如,如果您使用异步 I/O,您可能会收到来自随机线程的回调,而不是您原来的线程。

    使用锁是否存在性能问题?

    是的。它们在单线程应用程序中不是很大,但为什么要进行不需要的调用呢?

    ...如果这是一个很好的设计模式可以在未来遵循[?]

    随意锁定所有内容是一种糟糕的设计模式。如果您的代码被随机锁定弄得杂乱无章,然后您决定使用后台线程进行某些工作,那么您很可能会遇到死锁。在多个线程之间共享资源需要精心设计,越能隔离棘手的部分越好。

    【讨论】:

    • 所有 .NET 应用程序都是多线程的 - 至少有终结器线程在您的主线程旁边运行。
    • kokos,如果您的对象正在被终结,这意味着应用程序没有对它们的任何引用,因此应用程序无法同时修改您的对象,在编写终结器时,您可以假设您的对象对象只能从终结器线程访问,因此不需要锁定。
    【解决方案2】:

    这里的所有答案似乎都是正确的:锁的用处是阻止线程同时访问锁定的代码。然而,这个领域有很多微妙之处,其中之一是公共语言运行时会自动将锁定的代码块标记为关键区域

    代码被标记为关键的影响是,如果整个区域无法完全执行,运行时可能会认为您的整个应用程序域可能受到威胁,因此将其从内存中卸载。引用MSDN:

    例如,考虑一个在持有锁的同时尝试分配内存的任务。如果内存分配失败,中止当前任务并不足以保证 AppDomain 的稳定性,因为域中可能还有其他任务在等待同一个锁。如果当前任务终止,其他任务可能会死锁。

    因此,即使您的应用程序是单线程的,这对您来说也可能是一种危险。考虑到锁定块中的一种方法会引发异常,该异常最终不会在块内处理。即使异常在调用堆栈中冒泡时得到处理,您的代码关键区域也没有正常完成。谁知道 CLR 会如何反应?

    欲了解更多信息,请阅读this article on the perils of Thread.Abort()

    【讨论】:

    • 我不认为您对 CLR 的“关键区域”标记是正确的。这可能被 Monitor.Enter 使用,但用户的受保护代码没有被标记为这样。有关关键区域的深入了解,请参阅本文:msdn.microsoft.com/en-us/magazine/cc163716.aspx
    • 我看不出有什么问题。你能解释清楚一点吗?
    • @Michael Donohue:我不知道将东西标记为关键区域,但 lock 和 Monitor.Enter 实际上是相同的。 lock 只是Enter-try-stuff-finally-Exit 模式的语法糖。
    【解决方案3】:

    请记住,您的应用程序不像您想象的那样单线程可能是有原因的。 .NET 中的异步 I/O 可以很好地回调池线程,例如,一些不同的计时器类(但不是 Windows 窗体计时器)也是如此。

    【讨论】:

      【解决方案4】:

      一般来说,如果您的应用程序是单线程的,您将不会从 lock 语句中获得太多用处。不完全了解您的应用程序,我不知道它们是否有用 - 但我怀疑不是。此外,如果您的应用程序在任何地方都使用锁,我不知道我是否会对它在多线程环境中的工作充满信心 - 原始开发人员实际上知道如何开发多线程代码,还是他们只是在模糊的希望中到处添加锁定语句来解决问题?

      【讨论】:

      • 我对原始开发人员一无所知,我收到了它并说,我们需要进行这些更改。祝你好运。
      【解决方案5】:

      锁应该用在修改共享状态的代码周围,由其他线程同时修改的状态,并且那些其他线程必须采用相同的锁。

      锁实际上是一个内存访问序列化器,线程(获取锁)将等待锁进入,直到当前线程退出锁,因此内存访问被序列化。

      为了回答您的问题,单线程应用程序不需要锁,它确实有性能副作用。因为 C# 中的锁是基于内核同步对象的,并且您使用的每个锁都会创建从用户模式到内核模式的转换。

      如果您对多线程性能感兴趣,可以从MSDN threading guidelines 入手

      【讨论】:

        【解决方案6】:

        可能在使用锁定变量时遇到性能问题,但通常情况下,您会构建代码以尽量减少在“锁定”代码块中花费的时间长度。

        至于移除锁。这将取决于代码到底在做什么。即使它是单线程的,如果您的对象是作为单例实现的,那么您可能会有多个客户端同时使用它的一个实例(在内存中,在服务器上)..

        【讨论】:

          【解决方案7】:

          是的,使用锁时会有一些性能损失,但通常可以忽略不计。

          使用锁(或任何其他互斥语句或构造)通常只在多线程(您自己创建或来自您的调用者)有机会与对象交互并更改对象的多线程场景中才需要。基础状态或数据维护。例如,如果您有一个可由多个线程访问的集合,您不希望一个线程在另一个线程试图读取它时通过删除一个项目来更改该集合的内容。

          【讨论】:

            【解决方案8】:

            Lock(token) 仅用于标记不应在多个线程中同时运行的一个或多个代码块。如果您的应用程序是单线程的,那么它可以防止出现不存在的情况。

            锁定确实会导致性能下降,在执行代码之前添加指令以检查同时访问。它只应在必要时使用。

            【讨论】:

              【解决方案9】:

              请参阅question 关于 C# 中的“互斥”。然后看看thesetwo关于使用'lock(Object)'语句的具体问题。

              【讨论】:

                【解决方案10】:

                如果只有一个线程,那么在应用程序中设置锁是没有意义的,是的,这会影响性能,尽管确实需要相当多的调用才能使该命中累积成重要的东西。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-09-23
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-02-08
                  相关资源
                  最近更新 更多