C# 这是一份清单<;T>;属性线程安全? private List\T; 私有只读对象_syncLock=新对象(); 私有列表MyT { 得到 { 锁(同步锁) return_T.ToList(); } 设置 { 锁(同步锁) _T=数值; } }
是。您使用了一个成员变量作为锁,并确保它不能更改。那很好。是的,你看起来很安全。如果查看ToList()的定义,它是:C# 这是一份清单<;T>;属性线程安全? private List\T; 私有只读对象_syncLock=新对象(); 私有列表MyT { 得到 { 锁(同步锁) return_T.ToList(); } 设置 { 锁(同步锁) _T=数值; } },c#,list,thread-safety,C#,List,Thread Safety,是。您使用了一个成员变量作为锁,并确保它不能更改。那很好。是的,你看起来很安全。如果查看ToList()的定义,它是: private List<T> _T; private readonly object _syncLock = new object(); private List<T> MyT { get { lock (_syncLock) re
private List<T> _T;
private readonly object _syncLock = new object();
private List<T> MyT
{
get
{
lock (_syncLock)
return _T.ToList<T>();
}
set
{
lock (_syncLock)
_T = value;
}
}
公共静态列表列表(此IEnumerable源代码)
{
if(source==null)
{
抛出错误。ArgumentNull(“源”);
}
返回新列表(源);
}
因此,本质上,您正在创建一个新列表,其中包含旧列表的元素,所有这些元素都位于您提供的锁之下,从而使其具有线程安全性
现在,列表的内容将是两个列表中相同的引用,因此它不会保护您不更改列表中存储的原始对象,它只保护列表本身。不,它不是线程安全的。请看以下代码:
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
return new List<TSource>(source);
}
静态MyClass sharedInstance=。。。;
//创建一个列表
var list=新列表();
//共享列表
sharedInstance.MyT=列表;
//列表现在已共享,此调用不是线程安全的。
增加(5);
问题在于,您允许使用者引用内部数据结构。您可以按如下方式解决此问题:
static MyClass<int> sharedInstance = ...;
// Create a list
var list = new List<int>();
// Share the list
sharedInstance.MyT = list;
// list is now shared, this call is not thread-safe.
list.Add(5);
私有列表MyT
{
得到
{
锁(同步锁)
return_T.ToList();
}
设置
{
var copy=value.ToList();
锁(同步锁)
_T=复制;
}
}
Exact dupe:假设您不会根据当前值更新当前值。。。是的,它是线程安全的。但是,如果您有任何代码看起来像“myTSObj.MyT=myTSObj.MyT+1”,那么没有。。。真的不是。在这种情况下,您已经对竞争条件敞开了大门。我认为修改原始列表会修改ToList()制作的副本,而不是原始列表是安全的。这将被分配为新成员。然而,你仍然可以有比赛条件。具体如何?这是一个列表,不像你的其他评论中那样是一个int。是的,但想法还是一样的。假设您的逻辑将一个项目添加到列表中,如果该项目在列表中不存在。但是,运行相同逻辑的另一个线程执行相同的操作。有可能多次以相同的值结束。为了避免这种情况,你的逻辑应该是锁定对象,因为它不可能避免所有这样的情况;然而,我相信这不是一个特定于这种逻辑的竞争条件。相反,它是特定于使用类的。此逻辑使列表属性线程安全。竞态条件是使用code.True时的一个问题。我同意这一点,但问题是“这是线程安全的吗”,答案是否定的。它不可能是线程安全的,因为他没有定义需要线程安全的用例。如果用例是“我希望能够读取整个列表,并在不混淆其他类的情况下将值分配给新列表的引用”,那么是的。它是线程安全的。否则,它真的不是。没关系。我将其用于值类型。仅通过查看ToList()
方法,无法得出线程安全的结论。事实上,当向这个方法提供一个实例时,在下面更改它会破坏它(查看List.ctor)。看看我的答案。这里我举一个例子来说明如何引入竞争条件。您需要锁定集合吗?因为这只是一个参考assignment@EpiX:严格来说不是。拥有锁的优点是它增加了一个内存屏障。这将清除缓存并防止其他线程获取旧的(已缓存的)引用。因此,即使在调用set
之后,它也会阻止其他线程获取旧值。但是由于get
中已经使用了lock
,因此set
中的lock
是多余的。然而,我确实喜欢在set和get中使用lock,因为这在大多数情况下是必需的,并且使代码在这种情况下不那么混乱。如果我们要从集合
中删除锁
,我们必须明确说明为什么不需要该锁
private List<T> MyT
{
get
{
lock (_syncLock)
return _T.ToList<T>();
}
set
{
var copy = value.ToList();
lock (_syncLock)
_T = copy;
}
}