Ian*_*oyd 7 .net multithreading
给定:禁止从一个线程构造ADO Connection对象并将其提供给另一个线程.两个线程是不同的公寓,即使第一个线程永远不会再触摸它(甚至没有保持对它的引用!),这没关系.
该ADO Connection对象是由ThreadA创建的,ThreadA是唯一一个在任何情况下都可以使用该Connection对象的线程.
现在将"ADO Connection"替换为"ADO.NET Connection".是否适用同样的规则?
我知道.NET框架中的大多数对象都不是线程安全的.例如,SDK中的DictionaryEntry结构说:
线程安全此类型的
任何公共静态成员都是线程安全的.任何实例成员都不保证是线程安全的.
据我所知,不是线程安全意味着如果我要从不同的线程访问它,我必须同步对象的访问.这一切都很好,我可以确保一次只有一个线程访问该对象:
lock (myObject)
{
...
}
Run Code Online (Sandbox Code Playgroud)
但是有一些东西不仅仅是线程安全的.
在COM中,(某些)对象被绑定到创建它的" 公寓 ".一旦在一个公寓上构建了对象,就禁止您从另一个公寓访问它 - 无论您从多个同时进行的线程访问中保护该对象多少.
.NET中是否存在类似的概念?
我知道你被禁止从创建它的线程以外的线程访问控件 - 即使你以线程安全的方式使用它.这在MSDN上没有记录:
线程安全
只有以下成员是线程安全的:如果已经创建了控件的句柄,则为BeginInvoke,EndInvoke,Invoke,InvokeRequired和CreateGraphics.在后台线程上创建控件的句柄之前调用CreateGraphics会导致非法的跨线程调用.
当您从单个线程创建和使用异常时,没有提到控件抛出异常 - 当该线程不是应用程序启动时创建的第一个线程时.
但是任意物体呢?关于什么:
public class MyClass
{
int _number;
public int Number { get { return _number; } set { _number = value; } }
}
MyClass myObject = new MyClass();
Run Code Online (Sandbox Code Playgroud)
只要我同步访问myObject,两个线程就可以与它通信了吗?
同样适用于:
List<Object> sharedList = new List<Object>();
Run Code Online (Sandbox Code Playgroud)
两个线程可以与列表通信,只要它们不同时执行,通常使用:
lock (sharedList)
{
sharedList.Add(data);
}
Run Code Online (Sandbox Code Playgroud)
允许两个线程接触同一个对象?
同样适用于:
IAsyncResult ar = BeginSetLabelToTheValueINeed(label1);
...
EndSetLabelToTheValueINeed(ar);
Run Code Online (Sandbox Code Playgroud)
同样适用于:
//Fetch image on connection that is an existing DB transaction
public static Bitmap GetImageThumbnail(DbConnection conn, int imageID)
{
}
Run Code Online (Sandbox Code Playgroud)
被转换为异步委托模式:
//Begin fetching an image on connection that is an existing DB transaction
IAsyncResult ar = BeginGetImageThumbnuts(conn, imageID, callback, stateOjbect);
...
//Finish fetching an image on connection that is an existing DB transaction
Bitmap thumb = EndGetImageNumbthail(ar);
Run Code Online (Sandbox Code Playgroud)
人们开始讨论ADO.NET中的设计模式,而不是回答这个问题.请回答这个问题.如果他们混淆和分散你的松鼠脑子,请忽略这些例子.
小智 3
为赏金而射击!
好的,.NET 中的某些类/类框架具有单元绑定的方法,但仅限于设计。这意味着您必须专门编写代码才能执行此操作。默认情况下未设置。为此编写代码有点笨拙。您必须获取想要保留的线程 ID 并始终检查它。我认为除了用于跟踪 UI 线程的 UI 组件的工具之外,框架中没有任何工具可以让这变得简单。该支持框架可能深埋在 UI 堆栈中;我从未在任何 MSDN 文档中看到过它。
更常见的是设置线程单元。整个框架的许多部分在设计上都限制为 STA 线程 ( STAThreadAttribute )。这通常是因为它们在其核心深处接触了 COM 对象,而这些对象通常要求它们仅在 STA 线程中使用。例如,winforms 应用程序中的主线程使用 STAThreadAttribute 进行标记,以便每个 UI 组件只能在它们创建的同一个线程(即 UI 线程)内进行操作。有趣的是,WPF 中的 XamlReader 知道当前线程单元,甚至不会反序列化 UI 组件,除非其运行的线程是 STA 线程。
“标准 POCO 是否具有线程亲和性或关心它是否在 STA 或 MTA 线程中?” 答案是否定的,除非您对其进行编码或使用 STAThreadAttribute 标记其方法,或者该对象是在调用堆栈中实例化的,而该调用堆栈在其层次结构中的某个位置具有使用 STAThreadAttribute 标记的方法。
“ADO.NET 对象是否具有线程关联性,并且它是否具有单元感知能力?”
好吧,DbConnection 继承自 Component,而 Component 又继承了 MarshalByRefObject。组件、MBRO、DbConnection 及其后代(即SqlConnection)不包含用STAThreadAttribute 标记的方法。快速检查它们的主要依赖项(例如 DbTransaction)并没有发现这些类的任何方法也仅标记为 STA。因此他们不了解公寓。
使用 Reflector 快速浏览并没有发现任何明显的线程 ID 监视。DbConnection 确实依赖于 Transaction,这似乎是这群中最有可能的线程爱好者。
我拿出 Reflector 并检查了其中的一些类。
DbConnection - 不关心当前线程的 ID
SqlConnection - 在调试 SqlTransaction 时进行一些线程跟踪
- 有一个名为“ZombieCheck”的函数,但没有线程监视
DbTransaction - 这里什么也没有。
所以我认为可以肯定地说 ADO.NET 对象不具有线程关联性。我什至没有看到他们使用Thread.Begin/EndThreadAffinity。
编辑
正如 Jeremy 在评论中指出的那样,我一直在谈论用属性标记的类,而不是类的方法。巨大的错误。我搞砸了,给人的印象是我只查看了类定义而不是整个类。
我对此进行了编辑以澄清,并且让我在这里说得绝对清楚:System.Data 中没有类方法用 STAThreadAttribute 标记。您可以使用以下 hacky 代码亲自检查这一点:
SqlConnection sc = new SqlConnection();
var data = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => x.FullName.StartsWith("System.Data,")).First();
var dtypes = data.GetTypes();
var results = new List<string>();
foreach (var type in dtypes)
foreach (var method in type.GetMethods())
foreach (var attr in
method.GetCustomAttributes(true).OfType<System.Attribute>())
{
results.Add(string.Format("{0} {1} {2}",
type.Name, method.Name, attr.GetType().Name));
if (attr.GetType() == typeof(STAThreadAttribute))
throw new Exception("SHIII");
}
Run Code Online (Sandbox Code Playgroud)
另外,winforms 中的 UI 类也不使用 STAThreadAttribute;它是使用此属性的应用程序(正如 Jeremy 再次指出的那样)。
有一种特殊情况,我遇到过一个有公寓意识的班级。XamlReader 反序列化 xaml 并创建 WPF UI 类,当在 MTA 线程上调用其 Load 方法时,将引发异常。它甚至没有在文档中,这很烦人......所以,同样,类默认情况下不识别公寓;您必须以这种方式对其进行编码,或者必须通过在包含由 STAThreadAttribute 标记的方法的方法链中更新它们来使它们的实例感知。呼。这些东西解释起来比理解更难...
归档时间: |
|
查看次数: |
928 次 |
最近记录: |