【问题标题】:What is C#'s version of the GIL?什么是 C# 的 GIL 版本?
【发布时间】:2011-04-30 21:17:02
【问题描述】:

在当前的 CPython 实现中,有一个称为“GIL”或“全局解释器锁”的对象。它本质上是一个互斥体,可以防止两个 Python 线程同时执行 Python 代码。这可以防止两个线程破坏 Python 解释器的状态,但也可以防止多个线程真正一起执行。本质上,如果我这样做:

# Thread A
some_list.append(3)
# Thread B
some_list.append(4)

我不能破坏列表,因为在任何给定时间,只有一个线程在执行,因为它们必须持有 GIL 才能这样做。现在,列表中的项目可能会以某种不确定的顺序添加,但关键是列表没有损坏,并且总是会添加两件事。

所以,现在到 C#。 C# 本质上面临与 Python 相同的问题,那么,C# 是如何防止这种情况的呢?如果有人知道的话,我也很想听听 Java 的故事。


澄清:我对没有显式锁定语句会发生什么很感兴趣,尤其是对 VM 而言。我知道 Java 和 C# 都存在锁定原语——它们也存在于 Python 中:GIL 不用于多线程代码,除了保持解释器正常。我对上面的直接等价物感兴趣,所以,在 C# 中,如果我能记住的话...... :-)

List<String> s;
// Reference to s is shared by two threads, which both execute this:
s.Add("hello");
// State of s?
// State of the VM? (And if sane, how so?)

这是另一个例子:

class A
{
    public String s;
}
// Thread A & B
some_A.s = some_other_value;

// some_A's state must change: how does it change?
// Is the VM still in good shape afterwards?

我不想编写糟糕的 C# 代码,我理解 lock 语句。即使在 Python 中,GIL 也不会为您提供神奇的多线程代码:您仍然必须锁定共享资源。但是 GIL 可以防止 Python 的“VM”被破坏——我感兴趣的是这种行为。

【问题讨论】:

  • 您可能对此comparison of Python and Clojure 的“并发”部分感兴趣,以讨论 GIL 以及如何在没有 GIL 的语言中处理并发问题。
  • 我的理解是,在哪类问题更难解决之间进行权衡。请参阅下面的答案。

标签: c# java python gil


【解决方案1】:

大多数其他支持线程的语言都没有 Python GIL 的等价物;它们要求您隐式或显式地使用互斥锁。

【讨论】:

  • 此外,我什至不会考虑用像 GIL 这样的语言编写多线程代码。这就像一个闪烁的霓虹灯,上面写着“多线程太难了;我们放弃了。”
  • 我不会考虑用一种自身无法保持其基本数据结构一致并且依赖程序员到处加锁的语言编写多线程程序。
  • 您宁愿使用一种不可能同时发生多件事情的语言?这不是一个多线程程序。不,“到处加锁”并不是唯一的选择。
  • “VM 的状态”到底是什么意思?唯一重要的状态是被多个线程操作的特定对象的状态。如果 VM 需要进行垃圾回收,它会暂停所有线程,直到 GC 完成。 VM 不能被多个线程破坏一个对象实例 - 只有该对象的数据可以被破坏。只在需要的地方加锁,而不是对所有东西都加锁,可以显着提高性能。
  • @JoelMueller:在 Python 语言 的什么地方指定了 GIL?
【解决方案2】:

使用锁,你可以这样做:

lock(some_list)
{
    some_list.Add(3);
}

在线程 2 中:

lock(some_list)
{
    some_list.Add(4);
}

lock 语句确保lock 语句中的对象,在本例中为some_list,一次只能由单个线程访问。请参阅http://msdn.microsoft.com/en-us/library/c5kehkcz(VS.80).aspx 了解更多信息。

【讨论】:

  • 我可以,Python 也有类似的 lock 语句。我对未完成锁定的情况更感兴趣:是否有可能破坏底层 VM?
  • @Thanatos:不是虚拟机,而是数据结构。
  • 结果是无法保证会发生什么。它不会破坏虚拟机的状态。但是,它可能会破坏您正在调用方法的实例的状态。如果在 MSDN 页面中没有特别说明,一般规则是实例方法必须包含在 lock 中,而静态方法不必包含在 lock 中。
  • 锁不保证锁语句里面的对象只能被单线程访问。它将代码块标记为临界区,并确保没有其他线程进入该临界区。您可以在锁之外修改列表。我会更改您对 lock 语句的描述。
  • @Thanatos 你在说什么虚拟机状态?在 C# 中,几乎所有事情都是按线程完成的。线程甚至有自己的堆来分配内存。
【解决方案3】:

C# 没有与 Python 等效的 GIL。

虽然他们面临同样的问题,但他们的设计目标使他们 不同。

使用 GIL,CPython 确保追加列表等操作 从两个线程很简单。这也 意味着它只允许一个 线程随时运行。这 使列表和字典线程安全。虽然这使工作 更简单直观,它使它 更难利用多线程 多核优势。

没有 GIL,C# 则相反。 它确保完整性的负担由开发者承担 程序,但允许您采取 运行多个线程的优势 同时。

根据其中一项讨论-

CPython 中的 GIL 纯粹是一种设计选择 一个大锁与每个对象一个锁 和同步以确保对象保持一致状态。 这包括一个权衡 - 放弃全部权力 多线程。

大多数问题都没有这个缺点 并且有一些库可以帮助您专门解决这个问题 必需的。 这意味着对于某一类问题,利用 多核是 传递给开发者,让休息可以享受更简单,更直观 接近。

注意:IronPython 等其他实现没有 GIL。

【讨论】:

  • @Daniel DiPaolo:非常感谢。不知何故,当我选择文本并尝试引用它时,引用对我不起作用。
  • 奇怪,秘密在于它只是 &gt; 在行的开头强制块引用而不是代码格式。请注意,您仍然可以在块引用中进行代码格式化,方法是在行首用&gt; 适当地间隔它。
【解决方案4】:

查看您正在讨论的课程的documentation for the Java equivalent 可能会有所帮助:

注意这个实现是不同步的。如果多个线程同时访问一个ArrayList实例,并且至少有一个线程在结构上修改了列表,它必须外部同步。 (结构修改是添加或删除一个或多个元素,或显式调整后备数组大小的任何操作;仅设置元素的值不是结构修改。)这通常通过同步一些自然封装的对象来完成列表。如果不存在这样的对象,则应使用Collections.synchronizedList 方法“包装”该列表。这最好在创建时完成,以防止意外不同步地访问列表:

List list = Collections.synchronizedList(new ArrayList(...));

这个类的 iterator 和 listIterator 方法返回的迭代器是fail-fast:如果列表在迭代器创建后的任何时间被结构修改,除了通过迭代器自己的删除或添加方法,迭代器将抛出一个ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

请注意,无法保证迭代器的快速失败行为,因为一般来说,在存在不同步的并发修改的情况下无法做出任何硬保证。快速失败的迭代器会尽最大努力抛出ConcurrentModificationException。因此,编写一个依赖此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该只用于检测错误

【讨论】:

    【解决方案5】:

    大多数复杂的数据结构(例如列表)在不锁定多个线程的情况下使用时可能会损坏。

    由于引用的更改是原子的,因此引用始终是有效的引用。

    但是在与安全关键代码交互时会出现问题。因此,关键代码使用的任何数据结构大多是以下之一:

    • 无法从不受信任的代码访问,并且被受信任的代码正确锁定/使用
    • 不可变(字符串类)
    • 使用前复制(值类型参数)
    • 以可信代码编写并使用内部锁定来保证安全状态

    例如,关键代码不能信任可从不受信任代码访问的列表。如果它在 List 中传递,它必须创建一个私有副本,对副本进行前置条件检查,然后对副本进行操作。

    【讨论】:

      【解决方案6】:

      我要大胆猜测一下这个问题真正是什么意思......

      在 Python 中,解释器中的数据结构会损坏,因为 Python 使用了一种引用计数形式。

      C# 和 Java 都使用垃圾回收,实际上它们确实在进行完整堆回收时使用全局锁。

      数据可以在没有锁的情况下在“代”之间进行标记和移动。但要真正清理它,一切都必须停止。希望是一个非常短暂的停止,但一个完整的停止。

      这是一个关于 2007 年的 CLR 垃圾收集的有趣链接:
      http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry

      【讨论】:

        猜你喜欢
        • 2010-11-22
        • 1970-01-01
        • 1970-01-01
        • 2015-03-28
        • 2011-04-27
        • 2010-09-19
        • 2020-07-12
        • 1970-01-01
        • 2018-06-22
        相关资源
        最近更新 更多