什么是C#的GIL版本?

时间:2010-10-26 17:29:54

标签: c# java python gil

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

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

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

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


澄清:我对没有显式锁定语句的情况感兴趣,特别是对VM。我知道Java和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”被破坏 - 这是我感兴趣的行为。

6 个答案:

答案 0 :(得分:11)

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

答案 1 :(得分:4)

使用锁定,你会这样做:

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

答案 2 :(得分:3)

C#没有与GIL等效的GIL。

  

虽然他们面临同样的问题,但他们的设计目标也是如此   不同。

     

使用GIL,CPython 可确保将操作添加为列表   从两个线程很简单。哪个呢   意味着它只允许一个   线程随时运行。这个   使列表和词典线程安全。虽然这使得工作成功   它更简单直观   更难利用多线程   多核的优势。

     

没有GIL,C#则相反。 确保诚信的负担在开发者身上   程序,但允许你采取   运行多个线程的优势   同时进行。

根据其中一个讨论 -

  

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

     

一直以来,大多数问题都没有受到这种劣势的影响      并且有些库可以帮助您专门解决此问题   需要。       这意味着对于某类问题,利用这个问题的负担   多核是       传递给开发人员,以便休息可以享受更简单,直观   方法

注意:像IronPython这样的其他实现没有GIL。

答案 3 :(得分:1)

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

  

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

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

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

     

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

答案 4 :(得分:0)

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

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

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

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

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

答案 5 :(得分:0)

我会猜测真正的含义是什么......

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

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

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

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