`System.MulticastDelegate` 是线程安全的吗?
Are `System.MulticastDelegate`'s thread-safe?
我正在寻找可能对此了解更多的人,我的直觉告诉我答案是 "no, it is not thread-safe",但我想确定一下。
为了说明我的问题,我提供了一些背景信息class
public class MyContext
{
private readonly object _lock = new object();
public delegate bool MyDelegate(MyContext context);
private MyDelegate _multicastDelegate;
public MyContext()
{
_multicastDelegate = null;
}
public void AddDelegate(MyDelegate del)
{
lock(_lock)
{
_multicastDelegate += del;
}
}
public void RemoveDelegate(MyDelegate del)
{
lock(_lock)
{
_multicastDelegate += del;
}
}
public void Go()
{
_multicastDelegate.Invoke(this);
}
}
编辑:我在上面的例子中添加了锁,但这真的不是我的问题的重点。
我试图更好地理解保存调用列表的数组是否是线程安全的。坦率地说,我不清楚这一切是如何组合在一起的,如果能提供一些帮助,我们将不胜感激。
根据我找到的文档,唯一没有提供真正见解的引述如下:
A MulticastDelegate has a linked list of delegates, called an invocation list, consisting of one or more elements. When a multicast delegate is invoked, the delegates in the invocation list are called synchronously in the order in which they appear. If an error occurs during execution of the list then an exception is thrown.
https://msdn.microsoft.com/en-us/library/system.multicastdelegate.aspx
提前致谢。
Delegates 是 不可变的 。您永远不会更改代表。任何看似改变委托的方法实际上都是在创建一个新实例。
Delegates are immutable; once created, the invocation list of a delegate does not change.
因此无需担心在调用委托时调用列表可能会更新。
然而,您确实必须提防,但在您的方法中未能做到的是委托实际上可能是 null
。
(new MyContext()).Go();
会导致异常。您过去必须通过将值读入局部变量、测试它是否为 null 然后使用它调用来防止这种情况。它现在可以更容易地解析为:
public void Go()
{
_multicastDelegate?.Invoke(this);
}
MSDN 文档使用的线程安全定义是指正确同步的代码。它通常不说明它同步的是什么,但它可以是静态成员的 class 对象,实例成员的实例对象,或者它可以是一些内部对象,例如许多中的 SyncRoot
集合类型。
虽然委托是不可变的,但您仍然必须正确同步。 .NET 和 C# 与 Java 不同,不保证安全发布,因此如果不保证同步,您可以在其他线程 1 中观察到一个部分初始化的对象。
要使您的代码线程安全,您只需要在从委托字段读取时也使用 _lock
,但您可以在锁外调用 Invoke
,登陆委托有责任保持自己的线程安全。
public class MyContext
{
private readonly object _lock = new object();
public delegate bool MyDelegate(MyContext context);
private MyDelegate _delegate;
public MyContext()
{
}
public void AddDelegate(MyDelegate del)
{
lock (_lock)
{
_delegate += del;
}
}
public void RemoveDelegate(MyDelegate del)
{
lock (_lock)
{
// You had a bug here, +=
_delegate -= del;
}
}
public void Go()
{
MyDelegate currentDelegate;
lock (_lock)
{
currentDelegate = _delegate;
}
currentDelegate?.Invoke(this);
}
}
- Microsoft 的 .NET Framework 实现总是进行易失性写入(或者他们这么说),这会隐式地为您提供安全发布,但我个人并不依赖于此。
我正在寻找可能对此了解更多的人,我的直觉告诉我答案是 "no, it is not thread-safe",但我想确定一下。
为了说明我的问题,我提供了一些背景信息class
public class MyContext
{
private readonly object _lock = new object();
public delegate bool MyDelegate(MyContext context);
private MyDelegate _multicastDelegate;
public MyContext()
{
_multicastDelegate = null;
}
public void AddDelegate(MyDelegate del)
{
lock(_lock)
{
_multicastDelegate += del;
}
}
public void RemoveDelegate(MyDelegate del)
{
lock(_lock)
{
_multicastDelegate += del;
}
}
public void Go()
{
_multicastDelegate.Invoke(this);
}
}
编辑:我在上面的例子中添加了锁,但这真的不是我的问题的重点。
我试图更好地理解保存调用列表的数组是否是线程安全的。坦率地说,我不清楚这一切是如何组合在一起的,如果能提供一些帮助,我们将不胜感激。
根据我找到的文档,唯一没有提供真正见解的引述如下:
A MulticastDelegate has a linked list of delegates, called an invocation list, consisting of one or more elements. When a multicast delegate is invoked, the delegates in the invocation list are called synchronously in the order in which they appear. If an error occurs during execution of the list then an exception is thrown.
https://msdn.microsoft.com/en-us/library/system.multicastdelegate.aspx
提前致谢。
Delegates 是 不可变的 。您永远不会更改代表。任何看似改变委托的方法实际上都是在创建一个新实例。
Delegates are immutable; once created, the invocation list of a delegate does not change.
因此无需担心在调用委托时调用列表可能会更新。
然而,您确实必须提防,但在您的方法中未能做到的是委托实际上可能是 null
。
(new MyContext()).Go();
会导致异常。您过去必须通过将值读入局部变量、测试它是否为 null 然后使用它调用来防止这种情况。它现在可以更容易地解析为:
public void Go()
{
_multicastDelegate?.Invoke(this);
}
MSDN 文档使用的线程安全定义是指正确同步的代码。它通常不说明它同步的是什么,但它可以是静态成员的 class 对象,实例成员的实例对象,或者它可以是一些内部对象,例如许多中的 SyncRoot
集合类型。
虽然委托是不可变的,但您仍然必须正确同步。 .NET 和 C# 与 Java 不同,不保证安全发布,因此如果不保证同步,您可以在其他线程 1 中观察到一个部分初始化的对象。
要使您的代码线程安全,您只需要在从委托字段读取时也使用 _lock
,但您可以在锁外调用 Invoke
,登陆委托有责任保持自己的线程安全。
public class MyContext
{
private readonly object _lock = new object();
public delegate bool MyDelegate(MyContext context);
private MyDelegate _delegate;
public MyContext()
{
}
public void AddDelegate(MyDelegate del)
{
lock (_lock)
{
_delegate += del;
}
}
public void RemoveDelegate(MyDelegate del)
{
lock (_lock)
{
// You had a bug here, +=
_delegate -= del;
}
}
public void Go()
{
MyDelegate currentDelegate;
lock (_lock)
{
currentDelegate = _delegate;
}
currentDelegate?.Invoke(this);
}
}
- Microsoft 的 .NET Framework 实现总是进行易失性写入(或者他们这么说),这会隐式地为您提供安全发布,但我个人并不依赖于此。