为什么 CurrentCulture 是 Thread 的属性?
当前文化信息(CurrentCulture 和/或 CurrentUICulture)是正在运行的线程的属性,这让我觉得这是一个奇怪的设计选择。至少,这样的事情的范围似乎应该在流程层面上一层楼。
但一旦你听到了基本原理,这些事情通常就有意义了。了解为什么 .NET 设计者认为 Thread 是放置此属性的正确位置可能会很有启发。
It strikes me as an odd design choice that the current culture information (CurrentCulture and/or CurrentUICulture) is a a property of the running thread. At the very least it seems like the scope of such a thing should be one level up, at the process level.
But these things usually make sense once you hear the rationale. It might be enlightening to find out why the .NET designers decided that Thread was the right place to put this property.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
首先,这并不是他们的选择。这个决定是在他们开始之前很久就做出的,文化是操作系统线程的一个属性。例如,请查看 SDK 文档中的 Get/SetThreadLocale() API 函数。
这并不能完全解释这一点,他们已经虚拟化了其他操作系统功能,尽管这个功能非常很难虚拟化,因为很多 API 都是文化敏感的,尤其是 COM 的。
下一个充分的理由是,改变整个流程的文化极其难以实施。这是一个无法解决的竞争条件。其他一些线程很可能正在格式化具有文化亲和力的数据。虽然锁可以阻止它在更改时使用区域性属性,但它不能阻止它在格式化调用链中间发生更改。这将要求他们采取某种全局锁定并在格式化作业期间保持它。死锁的可能性非常。
这还有另一方面,我认为这才是真正的问题。它与 Thread.ExecutionContext 属性相关。该框架使用它来将线程状态从一个线程“流”到另一个线程。非常晦涩,但对于将安全上下文之类的东西注入到工作线程中很重要。如果该环境也能融入文化,那么您就可以确保您启动的任何工作人员都具有您选择的相同文化而不是操作系统默认文化,那就太理想了。
事实并非如此,我实在不知道为什么。可能是因为我给出的第一个原因。然而,这确实使得改变线程的文化变得非常危险。可能导致的错误类型非常很微妙。就像在主线程中使用非操作系统默认区域性创建一个以字符串作为键的 SortedDictionary 一样。然后发现工作线程有时无法再找到东西了,因为字符串排序规则不同。
编辑:.NET 4.5 中的这个问题得到了一些缓解,它支持新的静态 CultureInfo.DefaultThreadCurrentCulture 和 DefaultThreadCurrentUICulture 属性。
EDIT2:文化现在如 .NET 4.6 第 4 段中所述流动。这应该可以缓解所有担忧。
For starters, it wasn't really their choice. That decision was made a long time before they got started, culture is a property of an operating system thread. Review the SDK docs for the Get/SetThreadLocale() API functions for example.
That doesn't completely explain it, they have virtualized other OS features, albeit that this one is very hard to virtualize because so many APIs are culture sensitive, especially COM ones.
The next good reason is because changing the culture process-wide is so extremely difficult to implement. It is an unsolvable race condition. Some other thread might well be in the middle of formatting data that has culture affinity. While a lock would work to prevent it from using culture properties just as it is changing, it can not prevent it from changing in the middle of a chain of formatting calls. It would require them to take some kind of global lock and hold it for the duration of the formatting job. Deadlock is very likely.
There's another aspect to this, one that I think is the real problem. It is related to the Thread.ExecutionContext property. The framework uses this to "flow" thread state from one thread to another. Very obscure but important to imbue things like the security context onto a worker thread. It would have been ideal if that context could also imbue the culture so that you can be sure that any of the workers you start have the same culture you selected and not the operating system default.
It doesn't, I don't really know why. Probably because the very first reason I gave. That does however make it very perilous to change a thread's culture. The kind of bugs that can cause are very subtle. Like creating a SortedDictionary with a string as the key in your main thread with a non-operating system default culture. Then finding out that a worker thread can't occasionally find stuff back anymore because the string sorting rules are different.
EDIT: there's some relief from this problem in .NET 4.5, it supports the new static CultureInfo.DefaultThreadCurrentCulture and DefaultThreadCurrentUICulture properties.
EDIT2: culture now flows as described in the 4th paragraph in .NET 4.6. This should alleviate all concerns.
我会尝试一下 - 也许是因为您需要使用不同文化的多个并发线程?如果您想到一个多语言的 ASP.NET 网站,您不希望该流程与一种语言绑定...网络请求可以是 EN-US 和另一种 fr-FR。
I'll take a shot - maybe because you would need multiple simultaneous thread using different culture? If you think of a multilingual asp.net website, you don't want the process to be tied to one language... a web request could be EN-US and another fr-FR.
窗口句柄本身是线程特定的。您永远不应该在不同线程的上下文中使用窗口句柄(对于顶级窗口、子控件等),因为实现窗口处理 (GDI) 的代码本身不是线程安全的。由于 UICulture 是特定于窗口的,这意味着它也可以在线程级别定义。
其他 GUI 方面也是线程特定的,例如活动窗口和焦点。虽然有一个 API,但我不记得名字了,它将不同线程的 UI 上下文连接在一起,以便在多个用户界面线程之间共享焦点、活动窗口。
Window handles are themselves thread specific. You should never use a window handle (for a top level window, child control etc) in the context of a different thread because the code that implements window handling (GDI) is not itself thread safe. As the UICulture is window specific it means it also becomes defined at the thread level.
Other GUI aspects are also thread specific, such as the active window and the focus. Although there is an API, I cannot remember the name, that joins the UI context from different threads together so that the focus, active window is shared between multiple user interface threads.
CurrentCulture 只是对 CultureInfo 的引用,因此允许它挂在线程上不会丢失太多内容(仅用于一个引用的内存)。同时,您可以获得允许用户精细控制每个线程的当前区域性的可能性 - 这在某些类型的应用程序中非常重要。
对于现代软件的非英语用户来说,如此细粒度的原因之一可能更为明显。我必须定期在 Windows 中切换键盘,并且可以使用一个全局键盘快捷键轻松完成此操作。在线程上使用 CultureInfo 使我们能够轻松地在 .NET 应用程序中实现类似的逻辑,而无需更改每个进程的信息。
我想到的用法示例:
我可能需要一个快捷方式,可以根据当前用户的请求在短时间内更改程序中的区域性(许多应用程序需要允许同一工作站上的多个用户,并且在同一会话)。但同一过程可能必须继续使用计算机上的默认 CultureInfo,例如使用始终相同的时间戳格式进行日志记录。
您可能还有一个报告解决方案,可以为一家跨国公司的多个分支机构转储报告,每个分支机构都有自己的文化。如果您可以更改每个线程的 CultureInfo,您的设计将受到更少的限制。
另一个原因是,如果您想要使用特定区域性写入文件,但您不想在写入函数中指定区域性(例如,因为这些函数已经被写入)。然后,您可以轻松更改该逻辑,而无需在其他线程上更改它,这些线程可能正在执行需要默认区域性的工作。
The CurrentCulture is nothing but a reference to a CultureInfo, so not much would be lost by allowing it to hang on the thread (just the memory for one reference). At the same time, you gain the possibility to allow users to finely control the current culture per thread - which turns out to be important in some kinds of applications.
One reason for having it this fine grained might be more obvious for non-English speaking users of modern software. I for one have to regularly switch the keyboard in Windows and am able to do that easily with one global keyboard shortcut. Having the CultureInfo on the thread allows us to easily implement similar logic in our .NET apps without having to change that info per process.
Usage examples that come to mind:
I might want a shortcut that would change the culture in my program for a short period of time on request for the current user (many applications need to allow several users on the same workstation and in the same session). But that same process might have to continue using the default CultureInfo on the machine to e.g. log with always the same timestamp format.
You might also have a report solution that would dump out reports for several branches of a global company, each in their own culture. Your design would be less constrained if you could change the CultureInfo per thread.
Another reason would be if you would want to write to a file using a particular culture, but you wouldn't want to specify the culture in your write functions (e.g. because those functions have already been written). Then you're able to change that logic easily without changing it on other threads which may be doing work that needs the default culture.