Plinq 语句在静态构造函数内陷入僵局
我遇到过这种情况,静态构造函数中的以下 plinq 语句陷入僵局:
static void Main(string[] args)
{
new Blah();
}
class Blah
{
static Blah()
{
Enumerable.Range(1, 10000)
.AsParallel()
.Select(n => n * 3)
.ToList();
}
}
仅当构造函数是静态时才会发生这种情况。 有人可以向我解释一下吗?
这是TPL错误吗?编译器?我?
I came across this situation where the following plinq statement inside static constructor gets deadlocked:
static void Main(string[] args)
{
new Blah();
}
class Blah
{
static Blah()
{
Enumerable.Range(1, 10000)
.AsParallel()
.Select(n => n * 3)
.ToList();
}
}
It happens only when a constructor is static.
Can someone explain this to me please.
Is it TPL bug? Compiler? Me?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
从静态构造函数调用线程代码通常是危险的。为了保证静态构造函数只执行一次,CLR在锁下执行静态构造函数。如果运行静态构造函数的线程等待辅助线程,则存在辅助线程由于某种原因也需要 CLR 内部锁的风险,并且程序将死锁。
下面是一个更简单的代码示例,演示了该问题:
ECMA CLI 规范保证以下内容:
因此,只要静态构造函数中没有操作阻塞线程,类型初始值设定项(即静态构造函数)就不会死锁。如果静态构造函数确实阻塞,则存在死锁风险。
It is generally dangerous to call threading code from a static constructor. In order to ensure that the static constructor executes only once, the CLR executes the static constructor under a lock. If the thread running the static constructor waits on a helper thread, there is a risk that the helper thread is going to need the CLR-internal lock for some reason too, and the program will deadlock.
Here is a simpler code sample that demonstrates the problem:
Section 10.5.3.3 "Races and deadlocks" of the ECMA CLI spec guarantees the following:
So, a type initializer (i.e., a static constructor) will not deadlock, provided that no operation in the static constructor blocks the thread. If the static constructor does block, it risks a deadlock.
虽然已经解释了为什么您不想在静态构造函数内进行线程工作,但我想我应该补充一点,“正确”的方法是使用 static
Lazy
。这也更加高效,因为生成这些资源的工作将被推迟到实际需要这些资源时。While the reason has already been explained as to why you wouldn't want to do threaded work inside a static constructor, I thought I'd add that the "right" way to do this instead would be with a static
Lazy<T>
. This is also more efficient as the work to generate those resources will be defferred until those resources are actually needed.就其价值而言,Mono 上不会出现这个问题:
您是否有 Windows 编译的二进制文件,以便我可以比较生成的 MSIL?我不相信这是一个仅库问题,而且我很好奇:)
比较 IL 有点混乱,所以我决定在两个平台上尝试这两个二进制文件。
呵呵,我恢复了我的旧Windows虚拟机只是为了测试这个:)
在Mono上运行VS编译的二进制文件是没有问题的。您可以使用 2.10.1 (http://www.go-mono.com/mono-downloads/download.html) 在 Windows 上尝试,仅 77.4Mb :)
(我在 linux 上使用了定制的 mono 2.11所以可能是功能支持尚未完成)
我还注意到,在 Windows 上运行时,CTRL-C 能够打破锁定。
如果我找到更多相关内容,我会发布。
更新2
嗯,安装Mono 绕着安装VSExpress 运行,甚至在Windows 上也是如此。安装 mono 在 4 分钟内完成,结果是:
没有死锁:) 现在剩下的就是等待 VSExpress 安装(永远)和安装调试工具(未知),然后破解它(可能要等到深夜) 。再见
For what its worth, the issue does not arise on Mono:
Do you have a windows compiled binary so I can compare the generated MSIL? I'm not convinced this is a library-only issue, and I'm curious :)
Comparing the IL was a bit messy, so I decided to just try both binaries on both platforms.
Hehe I revived my old Windows virtual machine just to test this :)
Running the VS compiled binaries on Mono is no problem. You could try it on windows using 2.10.1 (http://www.go-mono.com/mono-downloads/download.html), only 77.4Mb :)
(I used a custom built mono 2.11 on linux so it could be that the feature support is not complete yet)
I also noticed that when running on windows, a CTRL-C is able to break out of the lock.
Will post if I find some more to this.
Update 2
Well, installing Mono runs circles around installing installing VSExpress even on windows. Installing mono has finished in 4 minutes, and resulted in:
No deadlock :) Now all that remains is waiting for VSExpress to be installed (forever) and istall debugging tools (unknown) and than have a crack at it (probably till late night). CU later