这个 List<T> 属性线程安全吗?

3
    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;
        }
    }

精确复制:https://dev59.com/RW025IYBdhLWcg3wqX1o - Callum Rogers
在假设您不会基于当前值更新当前值的情况下...是的,它有一定的线程安全性。但是,如果您有任何类似于“myTSObj.MyT = myTSObj.MyT + 1”的代码,那么不...它真的不是。在这种情况下,您已经让自己面临竞态条件的风险。 - cwharris
我认为这是安全的,修改原始列表将会修改ToList()生成的副本,而不是原始列表。那个副本将会被分配作为新成员。 - James Michael Hare
3个回答

2

是的,您已经将成员变量用作锁,并确保它不能被更改。这将很好地工作。


1
然而,您仍然可能遇到竞态条件。 - cwharris
怎么做?这是一个列表,不像你之前评论中的整数。 - Nik
是的,但是想法仍然是一样的。假设您的逻辑会在列表中添加一个项目,如果该项目不存在于列表中。然而,另一个运行相同逻辑的线程也会执行相同的操作。可能会出现多次出现相同值的情况。为了避免这种情况,您的逻辑应该锁定对象,无法避免所有这样的情况。 - cwharris
抱歉我很无知,@Nik - cwharris
@Nik:我仍然同意@Xixonia之前的评论。如果可以以破坏线程安全的方式使用“属性”,那么它怎么能是线程安全的呢?请看我的回答,我给出了一个破坏线程安全的例子。 - Steven
显示剩余4条评论

2

不,它不是线程安全的。看一下下面的代码:

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);

问题在于您允许消费者引用内部数据结构。您可以按以下方式解决此问题:
private List<T> MyT
{
    get
    {
        lock (_syncLock)
            return _T.ToList<T>();
    }
    set
    {
        var copy = value.ToList();

        lock (_syncLock)
            _T = copy;
    }
}

你需要锁定这个集合吗?因为它只是一个引用赋值。 - Ryan Lee
@EpiX:严格来说不是这样的。拥有锁的好处在于它添加了一个内存屏障。这会清除缓存并防止其他线程获取旧(已缓存)引用。因此,即使调用了“set”,它也可以防止其他线程获取旧值。但由于“get”中已经使用了“lock”,所以“set”中的“lock”是多余的。然而,我喜欢在set和get中都使用锁,因为在大多数情况下这是必需的,并且可以使代码在这种情况下更加清晰易懂。如果我们从“set”中删除“lock”,那么我们必须明确注释为什么不需要该“lock”。 - Steven

1

是的,看起来你是安全的。如果你看一下ToList()的定义,它是:

public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    return new List<TSource>(source);
}

因此,您正在创建一个新列表,其中包含旧列表的元素,所有这些元素都在您提供的锁定下,从而使其具有线程安全性。

现在,列表的内容将是两个列表中相同的引用,因此它不能保护您免受更改存储在列表中的原始对象的影响,它只能保护列表本身。


没问题。我将其与值类型一起使用。 - Xaqron
仅仅通过查看 ToList<TSource>() 方法,你不能得出解决方案是线程安全的结论。实际上,当在此方法中提供一个实例并在其下进行更改时,它将被破坏(请查看 List<T>.ctor)。请参见我的答案。在这里,我举了一个引入竞态条件的示例。 - Steven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接