线程安全列表财产

发布于 2024-11-05 04:44:40 字数 312 浏览 0 评论 0 原文

我想要一个 List 的实现作为一个属性,毫无疑问可以线程安全地使用。

像这样的事情:

private List<T> _list;

private List<T> MyT
{
    get { // return a copy of _list; }
    set { _list = value; }
}

我似乎仍然需要返回集合的副本(克隆),因此如果我们在某个地方迭代集合并同时设置集合,则不会引发异常。

如何实现线程安全的集合属性?

I want an implementation of List<T> as a property which can be used thread-safely without any doubt.

Something like this:

private List<T> _list;

private List<T> MyT
{
    get { // return a copy of _list; }
    set { _list = value; }
}

It seems still I need to return a copy (cloned) of collection so if somewhere we are iterating the collection and at the same time the collection is set, then no exception is raised.

How to implement a thread-safe collection property?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

记忆之渊 2024-11-12 04:44:40

如果您的目标是 .Net 4,系统中有一些选项。 Collections.Concurrent 命名空间

在这种情况下,您可以使用 ConcurrentBag 而不是 List

If you are targetting .Net 4 there are a few options in System.Collections.Concurrent Namespace

You could use ConcurrentBag<T> in this case instead of List<T>

万劫不复 2024-11-12 04:44:40

即使它获得了最多的选票,通常也不能将 System.Collections.Concurrent.ConcurrentBag 作为 System.Collections.Generic.List 的线程安全替代品; 照原样(Radek Stromský 已经指出)未订购。

但是有一个名为 System.Collections.Generic.SynchronizedCollection 的类,自 .NET 3.0 起就已成为框架的一部分,但它隐藏在人们意想不到的位置它鲜为人知,而且可能你从来没有偶然发现过它(至少我从来没有)。

SynchronizedCollection 被编译为程序集System.ServiceModel.dll(它是客户端配置文件的一部分,但不是可移植类库的一部分)。

编辑:还有一个 NuGet 包“System.ServiceModel.Primitives”,自版本 ~4.6.0 起包含 System.Collections.Generic.SynchronizedCollection。< br>(归功于 Roi Shabtai)

Even as it got the most votes, one usually can't take System.Collections.Concurrent.ConcurrentBag<T> as a thread-safe replacement for System.Collections.Generic.List<T> as it is (Radek Stromský already pointed it out) not ordered.

But there is a class called System.Collections.Generic.SynchronizedCollection<T> that is already since .NET 3.0 part of the framework, but it is that well hidden in a location where one does not expect it that it is little known and probably you have never ever stumbled over it (at least I never did).

SynchronizedCollection<T> is compiled into assembly System.ServiceModel.dll (which is part of the client profile but not of the portable class library).

Edit: There is also a NuGet package "System.ServiceModel.Primitives" that since version ~4.6.0 contains the System.Collections.Generic.SynchronizedCollection<T>.
(Credits to Roi Shabtai)

无妨# 2024-11-12 04:44:40

C# 的 ArrayList 类有一个 同步方法。

var threadSafeArrayList = ArrayList.Synchronized(new ArrayList());

这将返回一个围绕 IList 的任何实例的线程安全包装器。所有操作都需要通过包装器来执行,以保证线程安全。

但请注意,这是通过锁定集合来实现的,这可能会影响性能。来自MS 文档

包装器的工作原理是在每次添加或删除操作时锁定整个集合。因此,每个尝试访问集合的线程都必须等待轮到它获取一个锁。 此过程不可扩展,并且可能会导致大型集合的性能显着下降。此外,该设计不受竞争条件的影响。

这对您来说可能不会影响交易,但值得记住。

C#'s ArrayList class has a Synchronized method.

var threadSafeArrayList = ArrayList.Synchronized(new ArrayList());

This returns a thread safe wrapper around any instance of IList. All operations need to be performed through the wrapper to ensure thread safety.

Note, however, that this works by locking the collection, which can have performance impact. From the MS docs:

The wrapper works by locking the entire collection on every add or remove operation. Therefore, each thread that's attempting to access the collection must wait for its turn to take the one lock. This process isn't scalable and can cause significant performance degradation for large collections. Also, the design isn't protected from race conditions.

This may not be a deal breaker for you– but it's worth keeping in mind.

习惯成性 2024-11-12 04:44:40

我认为制作一个示例 ThreadSafeList 类会很容易:

public class ThreadSafeList<T> : IList<T>
{
    protected List<T> _internalList = new List<T>();

    // Other Elements of IList implementation

    public IEnumerator<T> GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    protected static object _lock = new object();

    public List<T> Clone()
    {
        List<T> newList = new List<T>();

        lock (_lock)
        {
            _internalList.ForEach(x => newList.Add(x));
        }

        return newList;
    }
}

您只需在请求枚举器之前克隆列表,因此任何枚举都会处理运行时无法修改的副本。

I would think making a sample ThreadSafeList class would be easy:

public class ThreadSafeList<T> : IList<T>
{
    protected List<T> _internalList = new List<T>();

    // Other Elements of IList implementation

    public IEnumerator<T> GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    protected static object _lock = new object();

    public List<T> Clone()
    {
        List<T> newList = new List<T>();

        lock (_lock)
        {
            _internalList.ForEach(x => newList.Add(x));
        }

        return newList;
    }
}

You simply clone the list before requesting an enumerator, and thus any enumeration is working off a copy that can't be modified while running.

清风不识月 2024-11-12 04:44:40

即使接受的答案是 ConcurrentBag,我也不认为它在所有情况下都是列表的真正替代品,正如 Radek 对答案的评论所说:“ConcurrentBag 是无序集合,因此与 List 不同,它不能保证排序。此外,您无法通过索引访问项目”。

因此,如果您使用 .NET 4.0 或更高版本,解决方法可能是使用 ConcurrentDictionary,其中整数 TKey 作为数组索引,TValue 作为数组值。这是 Pluralsight 的 C# 并发集合课程 中推荐的替换列表的方法。 ConcurrentDictionary 解决了上面提到的两个问题:索引访问和排序(我们不能依赖排序,因为它是底层的哈希表,但当前的 .NET 实现保存了元素添加的顺序)。

Even accepted answer is ConcurrentBag, I don't think it's real replacement of list in all cases, as Radek's comment to the answer says: "ConcurrentBag is unordered collection, so unlike List it does not guarantee ordering. Also you cannot access items by index".

So if you use .NET 4.0 or higher, a workaround could be to use ConcurrentDictionary with integer TKey as array index and TValue as array value. This is recommended way of replacing list in Pluralsight's C# Concurrent Collections course. ConcurrentDictionary solves both problems mentioned above: index accessing and ordering (we can not rely on ordering as it's hash table under the hood, but current .NET implementation saves order of elements adding).

腹黑女流氓 2024-11-12 04:44:40

在 .NET Core(任何版本)中,您可以使用 ImmutableList,它具有 List 的所有功能。

In .NET Core (any version), you can use ImmutableList, which has all the functionality of List<T>.

裂开嘴轻声笑有多痛 2024-11-12 04:44:40

回复自我最初的答案以来的所有答案:

人们找到了这些解决方案:

  • 使用 SynchronizedCollection - 源代码 与下面的 SynchronizedList 几乎相同,具有相同的功能和问题,
  • 使用 ArrayList.Synchronized - 这将返回 a SyncIList,与下面的 SynchronizedList 相同,只是不
  • 通用列表的线程安全形式,每次访问时都克隆它 - 我相信使用 ThreadLocal 或 AsyncLocal 可以显着改进所提供的实现,但
  • 使用 Collections.Concurrent 命名空间中的各种类组合的任何性能测试仍然会失败 -这些包含 ICollection 的一些不错的选项,但不适用于用于索引访问的 IList
  • 使用 ConcurrentDictionary,以索引作为键,来模拟 IList - 这是我见过的最好的想法之一,但它并不是真的本着 IList 的精神,这意味着 O(1) 索引读取/追加复杂性和插入/删除的一些复杂性,以及 O(n) 空间复杂性。另外,IndexOf 和排序操作怎么样?

大多数针对 SynchronizedList 类的抱怨都与以下方面有关:

  • 锁机制的缓慢 - 性能计算根据使用列表的场景而有很大差异,因此对于
  • 使用 lock(object) 而不是使用 SemaphoreSlim 的 模糊要求,这是一个有效的选项- 好吧,这是我的抱怨:)但是修复代码以使用它是微不足道的,

可以实现更复杂的锁系统,例如单个行,行组等。这将开始看起来像是实现您自己的数据库,尽管。编写高性能集合是一门艺术,并且与您想要使用它的特定场景紧密结合。

我仍然相信,对于一般使用场景,这里的简单解决方案是最通用的。

C5 集合库是伟大集合设计的灵感之一,它如此处理并发性:

  • 没有并发集合(按设计),因为他们认为可以实现简单的锁定机制,但是当多个集合被访问时,场景会变得非常复杂。同时
  • “基于树的集合可以在修改时安全地枚举” - 他们推荐“模式 66”:“可以拍摄树的快照,然后枚举该快照的项目,同时修改原始树同时”

原始答案:

如果您查看 T 列表的源代码(https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877)你会注意到那里有一个类(当然是内部的 -为什么,微软,为什么?!?!)称为T的SynchronizedList。我将代码复制粘贴到此处:

   [Serializable()]
    internal class SynchronizedList : IList<T> {
        private List<T> _list;
        private Object _root;

        internal SynchronizedList(List<T> list) {
            _list = list;
            _root = ((System.Collections.ICollection)list).SyncRoot;
        }

        public int Count {
            get {
                lock (_root) { 
                    return _list.Count; 
                }
            }
        }

        public bool IsReadOnly {
            get {
                return ((ICollection<T>)_list).IsReadOnly;
            }
        }

        public void Add(T item) {
            lock (_root) { 
                _list.Add(item); 
            }
        }

        public void Clear() {
            lock (_root) { 
                _list.Clear(); 
            }
        }

        public bool Contains(T item) {
            lock (_root) { 
                return _list.Contains(item);
            }
        }

        public void CopyTo(T[] array, int arrayIndex) {
            lock (_root) { 
                _list.CopyTo(array, arrayIndex);
            }
        }

        public bool Remove(T item) {
            lock (_root) { 
                return _list.Remove(item);
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            lock (_root) { 
                return _list.GetEnumerator();
            }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator() {
            lock (_root) { 
                return ((IEnumerable<T>)_list).GetEnumerator();
            }
        }

        public T this[int index] {
            get {
                lock(_root) {
                    return _list[index];
                }
            }
            set {
                lock(_root) {
                    _list[index] = value;
                }
            }
        }

        public int IndexOf(T item) {
            lock (_root) {
                return _list.IndexOf(item);
            }
        }

        public void Insert(int index, T item) {
            lock (_root) {
                _list.Insert(index, item);
            }
        }

        public void RemoveAt(int index) {
            lock (_root) {
                _list.RemoveAt(index);
            }
        }
    }

我个人认为他们知道使用 SemaphoreSlim 可以创建,但没有到达它。

In reply to all of the answers since my original one:

People found these solutions:

  • use SynchronizedCollection - the source code is almost identical to the SynchronizedList below, with the same features and issues
  • use ArrayList.Synchronized - this will return a SyncIList, which is the same thing as the SynchronizedList below, only not generic
  • using a thread safe form of a list, where on every access you clone it - I believe the implementation presented could be improved significantly using ThreadLocal or AsyncLocal, but it would still fail any performance tests
  • use various combinations of classes in the Collections.Concurrent namespace - these contains some good options for ICollection, but NOT for IList for index access
  • use a ConcurrentDictionary<int, T>, with indexes as keys, to emulate an IList - this is one of the best ideas I've seen, but it's not really in the spirit of an IList, which implies O(1) index read/append complexity and some complexity in insert/deletes, as well as an O(n) space complexity. Also, what about IndexOf and sorting operations?

Most complaints against the SynchronizedList class were related to:

  • slowness of the lock mechanism - the performance calculation varies wildly based on the scenario where the list is used, so this is a valid option for a vague requirement
  • using lock(object) and not using SemaphoreSlim - OK, this is my complaint :) but fixing the code to use it is trivial

A more complex system of locks can be implemented, like for individual rows, groups of rows, etc. That will start to look like implementing your own database, though. Writing a high performance collection is an art and is very tightly coupled to the specific scenario you want to use it in.

I still believe that for a general use scenario the simple solution here is the most versatile.

The C5 collection library, which is one of the inspirations for great collection design, handles concurrency thus:

  • no concurrent collections (by design) because they feel a simple lock mechanism can be implemented, but scenarios get very complex when multiple collections are getting accessed at the same time
  • "A tree-based collection can be safely enumerated while modifying it" - where they recommend "pattern 66": "one can take a snapshot of the tree and then enumerate the items of that snapshot, while modifying the original tree at the same time"

Original answer:

If you look at the source code for List of T (https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877) you will notice there is a class there (which is of course internal - why, Microsoft, why?!?!) called SynchronizedList of T. I am copy pasting the code here:

   [Serializable()]
    internal class SynchronizedList : IList<T> {
        private List<T> _list;
        private Object _root;

        internal SynchronizedList(List<T> list) {
            _list = list;
            _root = ((System.Collections.ICollection)list).SyncRoot;
        }

        public int Count {
            get {
                lock (_root) { 
                    return _list.Count; 
                }
            }
        }

        public bool IsReadOnly {
            get {
                return ((ICollection<T>)_list).IsReadOnly;
            }
        }

        public void Add(T item) {
            lock (_root) { 
                _list.Add(item); 
            }
        }

        public void Clear() {
            lock (_root) { 
                _list.Clear(); 
            }
        }

        public bool Contains(T item) {
            lock (_root) { 
                return _list.Contains(item);
            }
        }

        public void CopyTo(T[] array, int arrayIndex) {
            lock (_root) { 
                _list.CopyTo(array, arrayIndex);
            }
        }

        public bool Remove(T item) {
            lock (_root) { 
                return _list.Remove(item);
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            lock (_root) { 
                return _list.GetEnumerator();
            }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator() {
            lock (_root) { 
                return ((IEnumerable<T>)_list).GetEnumerator();
            }
        }

        public T this[int index] {
            get {
                lock(_root) {
                    return _list[index];
                }
            }
            set {
                lock(_root) {
                    _list[index] = value;
                }
            }
        }

        public int IndexOf(T item) {
            lock (_root) {
                return _list.IndexOf(item);
            }
        }

        public void Insert(int index, T item) {
            lock (_root) {
                _list.Insert(index, item);
            }
        }

        public void RemoveAt(int index) {
            lock (_root) {
                _list.RemoveAt(index);
            }
        }
    }

Personally I think they knew a better implementation using SemaphoreSlim could be created, but didn't get to it.

表情可笑 2024-11-12 04:44:40

我建议任何在多线程场景中处理 List 的人看看 不可变集合 特别是 ImmutableArray

我发现它在以下情况下非常有用:

  1. 列表中的项目相对较少
  2. 读/写操作不多 大量
  3. 并发访问(即以读取模式访问列表的许多线程)

当您需要实现时也很有用某种类似事务的行为(即在失败时恢复插入/更新/删除操作)

I would suggest anyone dealing with a List<T> in multi-threading scenarios to take look at Immutable Collections in particular the ImmutableArray.

I've found it very useful when you have:

  1. Relatively few items in the list
  2. Not so many read/write operations
  3. A LOT of concurrent access (i.e. many threads that access the list in reading mode)

Also can be useful when you need to implement some sort of transaction-like behavior (i.e. revert an insert/update/delete operation in case of fail)

ま昔日黯然 2024-11-12 04:44:40

似乎许多发现此问题的人都想要一个线程安全的索引动态大小的集合。我所知道的最接近和最简单的事情是。

System.Collections.Concurrent.ConcurrentDictionary<int, YourDataType>

如果您想要正常的索引行为,这将要求您确保您的密钥正确递增。如果您小心的话,.count() 足以作为您添加的任何新键值对的键。

It seems like many of the people finding this are wanting a thread safe indexed dynamically sized collection. The closest and easiest thing I know of would be.

System.Collections.Concurrent.ConcurrentDictionary<int, YourDataType>

This would require you to ensure your key is properly incremented if you want normal indexing behavior. If you are careful .count() could suffice as the key for any new key value pairs you add.

捎一片雪花 2024-11-12 04:44:40

您还可以使用更原始的

Monitor.Enter(lock);
Monitor.Exit(lock);

锁使用(请参阅这篇文章C# 锁定在锁块中重新分配的对象)。

如果您期望代码中出现异常,那么这并不安全,但它允许您执行类似以下操作:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;

public class Something
{
    private readonly object _lock;
    private readonly List<string> _contents;

    public Something()
    {
        _lock = new object();

        _contents = new List<string>();
    }

    public Modifier StartModifying()
    {
        return new Modifier(this);
    }

    public class Modifier : IDisposable
    {
        private readonly Something _thing;

        public Modifier(Something thing)
        {
            _thing = thing;

            Monitor.Enter(Lock);
        }

        public void OneOfLotsOfDifferentOperations(string input)
        {
            DoSomethingWith(input);
        }

        private void DoSomethingWith(string input)
        {
            Contents.Add(input);
        }

        private List<string> Contents
        {
            get { return _thing._contents; }
        }

        private object Lock
        {
            get { return _thing._lock; }
        }

        public void Dispose()
        {
            Monitor.Exit(Lock);
        }
    }
}

public class Caller
{
    public void Use(Something thing)
    {
        using (var modifier = thing.StartModifying())
        {
            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("B");

            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("A");
        }
    }
}

这样做的好处之一是您将在一系列操作的持续时间内获得锁定(而不是锁定)每个操作)。这意味着输出应该以正确的块形式出现(我对此的使用是从外部进程将一些输出显示到屏幕上)

我真的很喜欢 ThreadSafeList 的简单性 + 透明度 + 它在阻止崩溃方面发挥了重要作用

You can also use the more primitive

Monitor.Enter(lock);
Monitor.Exit(lock);

which lock uses (see this post C# Locking an object that is reassigned in lock block).

If you are expecting exceptions in the code this is not safe but it allows you to do something like the following:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;

public class Something
{
    private readonly object _lock;
    private readonly List<string> _contents;

    public Something()
    {
        _lock = new object();

        _contents = new List<string>();
    }

    public Modifier StartModifying()
    {
        return new Modifier(this);
    }

    public class Modifier : IDisposable
    {
        private readonly Something _thing;

        public Modifier(Something thing)
        {
            _thing = thing;

            Monitor.Enter(Lock);
        }

        public void OneOfLotsOfDifferentOperations(string input)
        {
            DoSomethingWith(input);
        }

        private void DoSomethingWith(string input)
        {
            Contents.Add(input);
        }

        private List<string> Contents
        {
            get { return _thing._contents; }
        }

        private object Lock
        {
            get { return _thing._lock; }
        }

        public void Dispose()
        {
            Monitor.Exit(Lock);
        }
    }
}

public class Caller
{
    public void Use(Something thing)
    {
        using (var modifier = thing.StartModifying())
        {
            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("B");

            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("A");
        }
    }
}

One of the nice things about this is you'll get the lock for the duration of the series of operations (rather than locking in each operation). Which means that the output should come out in the right chunks (my usage of this was getting some output onto screen from an external process)

I do really like the simplicity + transparency of the ThreadSafeList + that does the important bit in stopping crashes

古镇旧梦 2024-11-12 04:44:40

我相信 _list.ToList() 会给你一份副本。如果需要,您也可以查询它,例如:

_list.Select("query here").ToList(); 

无论如何,msdn 说这确实是一个副本,而不仅仅是一个参考。哦,是的,您需要锁定 set 方法,正如其他人指出的那样。

I believe _list.ToList() will make you a copy. You can also query it if you need to such as :

_list.Select("query here").ToList(); 

Anyways, msdn says this is indeed a copy and not simply a reference. Oh, and yes, you will need to lock in the set method as the others have pointed out.

从此见与不见 2024-11-12 04:44:40

查看原始示例,人们可能会猜测其目的是能够简单地用新列表替换列表。该房产的设置者告诉我们这件事。

Micrisoft 的线程安全集合用于安全地添加和删除集合中的项目。但是,如果在应用程序逻辑中您打算用新集合替换该集合,人们可能会再次猜测,不需要 List 的添加和删除功能。

如果是这种情况,简单的答案是使用 IReadOnlyList 接口:

 private IReadOnlyList<T> _readOnlyList = new List<T>();

    private IReadOnlyList<T> MyT
    {
       get { return _readOnlyList; }
       set { _readOnlyList = value; }
    }

在这种情况下不需要使用任何锁定,因为无法修改集合。如果在设置器中“_readOnlyList = value;”将被更复杂的东西所取代,然后可能需要锁。

Looking at the original sample one may guess that the intention was to be able to simply replace the list with the new one. The setter on the property tells us about it.

The Micrisoft's Thread-Safe Collections are for safely adding and removing items from collection. But if in the application logic you are intending to replace the collection with the new one, one may guess, again, that the adding and deleting functionality of the List is not required.

If this is the case then, the simple answer would be to use IReadOnlyList interface:

 private IReadOnlyList<T> _readOnlyList = new List<T>();

    private IReadOnlyList<T> MyT
    {
       get { return _readOnlyList; }
       set { _readOnlyList = value; }
    }

One doesn't need to use any locking in this situation because there is no way to modify the collection. If in the setter the "_readOnlyList = value;" will be replaced by something more complicated then the lock could be required.

青衫儰鉨ミ守葔 2024-11-12 04:44:40

基本上如果你想安全地枚举,你需要使用锁。

这方面请参考MSDN。 http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx

以下是您可能感兴趣的 MSDN 部分:

此类型的公共静态(在 Visual Basic 中为共享)成员是线程安全的。不保证任何实例成员都是线程安全的。

只要集合不被修改,List 就可以同时支持多个读取器。枚举集合本质上不是线程安全的过程。在枚举与一个或多个写访问争用的极少数情况下,确保线程安全的唯一方法是在整个枚举期间锁定集合。要允许多个线程访问集合以进行读写,您必须实现自己的同步。

Basically if you want to enumerate safely, you need to use lock.

Please refer to MSDN on this. http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx

Here is part of MSDN that you might be interested:

Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

A List can support multiple readers concurrently, as long as the collection is not modified. Enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with one or more write accesses, the only way to ensure thread safety is to lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.

意犹 2024-11-12 04:44:40

这是没有锁的线程安全列表的类

 public class ConcurrentList   
    {
        private long _i = 1;
        private ConcurrentDictionary<long, T> dict = new ConcurrentDictionary<long, T>();  
        public int Count()
        {
            return dict.Count;
        }
         public List<T> ToList()
         {
            return dict.Values.ToList();
         }

        public T this[int i]
        {
            get
            {
                long ii = dict.Keys.ToArray()[i];
                return dict[ii];
            }
        }
        public void Remove(T item)
        {
            T ov;
            var dicItem = dict.Where(c => c.Value.Equals(item)).FirstOrDefault();
            if (dicItem.Key > 0)
            {
                dict.TryRemove(dicItem.Key, out ov);
            }
            this.CheckReset();
        }
        public void RemoveAt(int i)
        {
            long v = dict.Keys.ToArray()[i];
            T ov;
            dict.TryRemove(v, out ov);
            this.CheckReset();
        }
        public void Add(T item)
        {
            dict.TryAdd(_i, item);
            _i++;
        }
        public IEnumerable<T> Where(Func<T, bool> p)
        {
            return dict.Values.Where(p);
        }
        public T FirstOrDefault(Func<T, bool> p)
        {
            return dict.Values.Where(p).FirstOrDefault();
        }
        public bool Any(Func<T, bool> p)
        {
            return dict.Values.Where(p).Count() > 0 ? true : false;
        }
        public void Clear()
        {
            dict.Clear();
        }
        private void CheckReset()
        {
            if (dict.Count == 0)
            {
                this.Reset();
            }
        }
        private void Reset()
        {
            _i = 1;
        }
    }

Here is the class for thread safe list without lock

 public class ConcurrentList   
    {
        private long _i = 1;
        private ConcurrentDictionary<long, T> dict = new ConcurrentDictionary<long, T>();  
        public int Count()
        {
            return dict.Count;
        }
         public List<T> ToList()
         {
            return dict.Values.ToList();
         }

        public T this[int i]
        {
            get
            {
                long ii = dict.Keys.ToArray()[i];
                return dict[ii];
            }
        }
        public void Remove(T item)
        {
            T ov;
            var dicItem = dict.Where(c => c.Value.Equals(item)).FirstOrDefault();
            if (dicItem.Key > 0)
            {
                dict.TryRemove(dicItem.Key, out ov);
            }
            this.CheckReset();
        }
        public void RemoveAt(int i)
        {
            long v = dict.Keys.ToArray()[i];
            T ov;
            dict.TryRemove(v, out ov);
            this.CheckReset();
        }
        public void Add(T item)
        {
            dict.TryAdd(_i, item);
            _i++;
        }
        public IEnumerable<T> Where(Func<T, bool> p)
        {
            return dict.Values.Where(p);
        }
        public T FirstOrDefault(Func<T, bool> p)
        {
            return dict.Values.Where(p).FirstOrDefault();
        }
        public bool Any(Func<T, bool> p)
        {
            return dict.Values.Where(p).Count() > 0 ? true : false;
        }
        public void Clear()
        {
            dict.Clear();
        }
        private void CheckReset()
        {
            if (dict.Count == 0)
            {
                this.Reset();
            }
        }
        private void Reset()
        {
            _i = 1;
        }
    }
白芷 2024-11-12 04:44:40

使用lock 语句来执行此操作。 (阅读此处了解更多信息。

private List<T> _list;

private List<T> MyT
{
    get { return _list; }
    set
    {
        //Lock so only one thread can change the value at any given time.
        lock (_list)
        {
            _list = value;
        }
    }
}

仅供参考,这可能并不完全符合您的要求 - 您可能想在代码中进一步锁定,但我不能这么认为。查看 lock 关键字并根据您的具体情况调整其使用。

如果需要,您可以使用 _list 变量在 getset 块中锁定,这将使因此读/写不能同时发生。

Use the lock statement to do this. (Read here for more information.)

private List<T> _list;

private List<T> MyT
{
    get { return _list; }
    set
    {
        //Lock so only one thread can change the value at any given time.
        lock (_list)
        {
            _list = value;
        }
    }
}

FYI this probably isn't exactly what your asking - you likely want to lock farther out in your code but I can't assume that. Have a look at the lock keyword and tailor its use to your specific situation.

If you need to, you could lock in both the get and set block using the _list variable which would make it so a read/write can not occur at the same time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文