c#集合类的线程安全(整理)

c# 实现线程安全List容器 实现: c#实现线程安全List,主要还是给夹锁。代码如下: using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime; using System.Text; namespace Concurrent { ///... 阅读详情

Queue<T> 类

MSDN的说法

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

只要不修改该集合,Queue<T> 就可以同时支持多个阅读器。即便如此,从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。

若要确保枚举过程中的线程安全,可以在整个枚举过程中锁定集合。若要允许多个线程访问集合以进行读写操作,则必须实现自己的同步。


.NET在4.0里面提供了专门的并行类,来弥补相关集合类的线程安全性。
System.Collections.Concurrent:

System.Collections.Concurrent 命名空间提供多个线程安全集合类。当有多个线程并发访问集合时,应使用这些类代替 System.CollectionsSystem.Collections.Generic 命名空间中的对应类型。


 说明
公共类BlockingCollection<T>为实现 IProducerConsumerCollection<T> 的线程安全集合提供阻塞和限制功能。
公共类ConcurrentBag<T>表示对象的线程安全的无序集合。
公共类ConcurrentDictionary<TKey, TValue>表示可由多个线程同时访问的键值对的线程安全集合。
公共类ConcurrentQueue<T>表示线程安全的先进先出 (FIFO) 集合。
公共类ConcurrentStack<T>表示线程安全的后进先出 (LIFO) 集合。
公共类OrderablePartitioner<TSource>表示将一个可排序数据源拆分成多个分区的特定方式。
公共类Partitioner提供针对数组、列表和可枚举项的常见分区策略。
公共类Partitioner<TSource>表示将一个数据源拆分成多个分区的特定方式。
 接口说明
公共接口IProducerConsumerCollection<T>定义供制造者/使用者用来操作线程安全集合的方法。此接口提供一个统一的表示(为生产者/消费者集合),从而更高级别抽象如 System.Collections.Concurrent.BlockingCollection<T> 可以使用集合作为基础的存储机制。
这里包含,字典类,队列和栈,对应的相关集合均不是安全的。
============================================================================
微软提供了一种线程安全的扩展手段:

即位于System.Collections命名空间下的集合,如Hashtable,ArrayList,Stack,Queue等.其均提供了线程同步的一个实现

集合线程同步的问题

public class Demo8
{
    ArrayList list = new ArrayList(1000000);
    public Demo8()
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(Task1));
        ThreadPool.QueueUserWorkItem(new WaitCallback(Task2));
    }

    public void Task1(object obj)
    {
        for (int i = 0; i < 500000; i++)
        {
            list.Add(i);
        }

        Console.WriteLine(DateTime.Now);
        Console.WriteLine("Task1 count {0}", list.Count);
    }

    public void Task2(object obj)
    {
        for (int i = 0; i < 500000; i++)
        {
            list.Add(i);
        }

        Console.WriteLine("Task2 count {0}", list.Count);
    }
}

image

与预期结果不同

调整为线程同步的集合

每种数据类型都包含一个静态的Synchronized方法,如

ArrayList list = ArrayList.Synchronized(new ArrayList(1000000));

调整后的结果

image 
以下为注意点:

  1. IsSynchronized判断集合是否为线程同步
  2. 其内部通过给SyncRoot属性加锁进行同步(即Monitor.Enter)

自己控制锁

public class Demo8
{
    ArrayList list = new ArrayList(1000000);
    public Demo8()
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(Task1));
        ThreadPool.QueueUserWorkItem(new WaitCallback(Task2));
    }

    public void Task1(object obj)
    {
        lock (list.SyncRoot)
        {
            for (int i = 0; i < 500000; i++)
            {
                list.Add(i);
            }
        } 
       
        Console.WriteLine(DateTime.Now);
        Console.WriteLine("Task1 count {0}", list.Count);
    }

    public void Task2(object obj)
    {
        lock (list.SyncRoot)
        {
            for (int i = 0; i < 500000; i++)
            {
                list.Add(i);
            }
        }
        Console.WriteLine("Task2 count {0}", list.Count);
    }
}

image

这样的结果显然好看点.内部实现是在Add方法中做锁定.效果自然不是很好.

其他集合类也是类似的操作

参考: 
http://www.cnblogs.com/Mainz/archive/2008/04/06/CSharp_HashTable_Dictionary_ArrayList_Threadsafe.html

泛型集合

可以看到原非泛型集合内部的线程同步集合,在每次操作均采用锁操作,但我们并非每个操作都需要锁,比如上面的2个线程操作.只需要2个锁就可以了,但使用内部集合的话则需要锁很多次,带来了性能问题.在.net 2.0泛型集合中,内部不再支持线程同步的集合,即使内部实现了线程同步的集合如List<T>的实现也为开发出来,即把lock的这个操作转嫁给开发者上面了.其实这样反而可以让我们更加了解线程同步的问题,如果真有需要的话,也可以自己实现一个了...


NET Framework 4 中的并行编程9---线程安全集合类

作者: 
李嘉良 
发表于: 
2012-02-29, 18:14 
评论: 
浏览: 
414 
RSS: 
0

在.Net 4中,新增System.Collections.Concurrent 命名空间中提供多个线程安全集合类,这些类提供了很多有用的方法用于访问集合中的元素,从而可以避免使用传统的锁(lock)机制等方式来处理并发访问集合.因此当有多个线程并发访问集合时,应首先考虑使用这些类代替 System.Collections 和 System.Collections.Generic 命名空间中的对应类型.具体如下:

1. ConcurrentQueue

表示线程安全的先进先出(FIFO)队列.代码如下:

           ConcurrentQueue<int> sharedQueue = new ConcurrentQueue<int>();

            for (int i = 0; i < 1000; i++)

            {

                sharedQueue.Enqueue(i);

            }

 

            int itemCount = 0;

 

            Task[] tasks = new Task[10];

            for (int i = 0; i < tasks.Length; i++)

            {

                tasks[i] = new Task(() =>

                {

                    while (sharedQueue.Count > 0)

                    {

                        int queueElement;

                        bool gotElement = sharedQueue.TryDequeue(out queueElement);

                        if (gotElement)

                        {

                            Interlocked.Increment(ref itemCount);

                        }

                    }

 

                });

                tasks[i].Start();

            }

 

            Task.WaitAll(tasks);

 

            Console.WriteLine("Items processed:{0}", itemCount);

            Console.WriteLine("Press Enter to finish");

            Console.ReadLine();

该类有两个重要的方法用来访问队列中的元素.分别是:

Ø TryDequeue 尝试移除并返回位于队列头开始处的对象.

Ø TryPeek尝试返回位于队列头开始处的对象但不将其移除.

现在,在多任务访问集合元素时,我们只需要使用TryDequeue或TryPeek方法,就可以安全的访问集合中的元素了.

2. ConcurrentStack

表示线程安全的后进先出(LIFO)栈.它也有几个有用的方法,分别是:

Ø TryPeek:尝试返回栈顶处的元素,但不移除.

Ø TryPop: 尝试返回栈顶处的元素并移除.

Ø TryPopRange: 尝试返回栈顶处开始指定范围的元素并移除.

在访问集合中的元素时,我们就可以上述方法.具体代码实例于上面的ConcurrentQueue类似,就不重复了.

3. ConcurrentBag

实现的是一个无序的集合类.代码如下:

            ConcurrentBag<int> sharedBag = new ConcurrentBag<int>();

            for (int i = 0; i < 1000; i++)

            {

                sharedBag.Add(i);

            }

 

            int itemCount = 0;

            Task[] tasks = new Task[10];

            for (int i = 0; i < tasks.Length; i++)

            {

                tasks[i] = new Task(() =>

                {

                   while(sharedBag.Count>0)

                    {

                        int queueElement;

                        bool gotElement = sharedBag.TryTake(out queueElement);

                       if (gotElement)

                            Interlocked.Increment(ref itemCount);

                    }

                });

 

                tasks[i].Start();

            }

 

            Task.WaitAll(tasks);

 

            Console.WriteLine("Items processed:{0}", itemCount);

            Console.WriteLine("Press Enter to finish");

            Console.ReadLine();

该类有两个重要的方法用来访问队列中的元素.分别是:

Ø TryTake 尝试移除并返回位于队列头开始处的对象.

Ø TryPeek尝试返回位于队列头开始处的对象但不将其移除.

4. ConcurrentDictionary

实现的是一个键-值集合类.它提供的方法有:

Ø TryAdd:尝试向集合添加一个键-值

Ø TryGetValue:尝试返回指定键的值.

Ø TryRemove:尝试移除指定键处的元素.

Ø TryUpdate:尝试更新指定键的值.

代码如下:

        class BankAccount

        {

            public int Balance

            {

                get;

                set;

            }

        }

 

 static void DictTest()

        {

            BankAccount account = new BankAccount();

            ConcurrentDictionary<objectint> sharedDict = new ConcurrentDictionary<objectint>();

 

            Task<int>[] tasks = new Task<int>[10];

            for (int i = 0; i < tasks.Length; i++)

            {

                sharedDict.TryAdd(i, account.Balance);

                tasks[i] = new Task<int>((keyObj) =>

                {

                    int currentValue;

                    bool gotValue;

                    for (int j = 0; j < 1000; j++)

                    {

                        gotValue = sharedDict.TryGetValue(keyObj, out currentValue);

                        sharedDict.TryUpdate(keyObj, currentValue + 1, currentValue);

                    }

                    int result;

                    gotValue = sharedDict.TryGetValue(keyObj, out result);

                    if (gotValue)

                    {

                        return result;

                    }

                    else

                    {

                        throw new Exception(String.Format("No data item available for key {0}", keyObj));

                    }

                }, i);

                tasks[i].Start();

            }

            for (int i = 0; i < tasks.Length; i++)

            {

                account.Balance += tasks[i].Result;

            }

 

            Console.WriteLine("Expected value {0}, Balance: {1}", 10000, account.Balance);

            Console.WriteLine("Press enter to finish");

            Console.ReadLine();

}

通过上述提供的安全类,我们可以方便的并发访问集合中的元素,而不需要以前的Synchronized方法或者lock(SyncRoot)等处理方式

http://blog.csdn.net/web718/article/details/5105578.
 

HashTable 中的 key/value均为 object类型,由包含集合元素的存储桶组成。存储桶是 HashTable中各元素的虚拟子组,与大多数集合中进行的搜索和检索相比,存储桶可令搜索和检索更为便捷。每一存储桶都与一个哈希代码关联,该哈希代码是使用哈希函数生成的并基于该元素的键。 HashTable的优点就在于其索引的方式,速度非常快。如果以任意类型键值访问其中元素会快于其他集合,特别是当数据量特别大的时候,效率差别尤其大。

HashTable的应用场合有:做对象缓存,树递归算法的替代,和各种需提升效率的场合。

       //  Hashtable sample 
 
    System.Collections.Hashtable ht  =     new  System.Collections.Hashtable();
 
     
//  --Be careful: Keys can't be duplicated, and can't be null---- 
 
    ht.Add(  1  "  apple  "  );
     ht.Add(
  2  "  banana  "  );
     ht.Add(
  3  "  orange  "  );
     
     
//  Modify item value: 
 
     if  (ht.ContainsKey(  1  ))
         ht[
  1  =     "  appleBad  "  ;
 
     
//  The following code will return null oValue, no exception 
 
     object  oValue  =  ht[  5  ];  
     
     
//  traversal 1: 
 
     foreach  (DictionaryEntry de  in  ht)
     
{
         Console.WriteLine(de.Key);
         Console.WriteLine(de.Value);
     }
 

 
     
//  traversal 2: 
 
    System.Collections.IDictionaryEnumerator d  =  ht.GetEnumerator();
     
while  (d.MoveNext())
     
{
         Console.WriteLine(
 " key:{0} value:{1} " , d.Entry.Key, d.Entry.Value);
     }
 

 
     
//  Clear items 
 
    ht.Clear();


Dictionary 和 HashTable内部实现差不多,但前者无需装箱拆箱操作,效率略高一点。

       //  Dictionary sample 
 
    System.Collections.Generic.Dictionary  <  int  string  >  fruits  =   
         
new  System.Collections.Generic.Dictionary  <  int  string  >  ();
 
     fruits.Add(
  1  "  apple  "  );
     fruits.Add(
  2  "  banana  "  );
     fruits.Add(
  3  "  orange  "  );
 
     
foreach  (  int  in  fruits.Keys)
     
{
         Console.WriteLine(
 " key:{0} value:{1} " , i, fruits);
     }
 

 
     
if  (fruits.ContainsKey(  1  ))
     
{
         Console.WriteLine(
 " contain this key. " );
     }

 

ArrayList 是一维变长数组,内部值为 object类型,效率一般:

 

       //  ArrayList 
 
    System.Collections.ArrayList list  =     new  System.Collections.ArrayList();
     list.Add(
  1  );  //  object type 
 
    list.Add(  2  );
     
for  (  int  =     0  ; i  <  list.Count; i  ++  )
     
{
         Console.WriteLine(list[i]);
     }



HashTable是经过优化的,访问下标的对象先散列过,所以内部是无序散列的,保证了高效率,也就是说,其输出不是按照开始加入的顺序,而 Dictionary遍历输出的顺序,就是加入的顺序,这点与 Hashtable不同。如果一定要排序 HashTable输出,只能自己实现:

       //  Hashtable sorting 
 
    System.Collections.ArrayList akeys  =     new  System.Collections.ArrayList(ht.Keys);  //  from Hashtable 
 
    akeys.Sort();  //  Sort by leading letter 
 
     foreach  (  string  skey  in  akeys)
     
{
         Console.Write(skey 
+   " : " );
         Console.WriteLine(ht[skey]);
     }

 

HashTable 与线程安全 

为了保证在多线程的情况下的线程同步访问安全,微软提供了自动线程同步的 HashTable:

如果 HashTable 要允许并发读但只能一个线程写 , 要这么创建 HashTable 实例 :

       //  Thread safe HashTable 
 
    System.Collections.Hashtable htSyn  =  System.Collections.Hashtable.Synchronized(  new System.Collections.Hashtable());

这样 , 如果有多个线程并发的企图写 HashTable 里面的 item, 则同一时刻只能有一个线程写 , 其余阻塞 ; 对读的线程则不受影响。

 

另外一种方法就是使用 lock 语句,但要 lock 的不是 HashTable ,而是其 SyncRoot ;虽然不推荐这种方法,但效果一样的,因为源代码就是这样实现的 :

//Thread safe
private static System.Collections.Hashtable htCache = new System.Collections.Hashtable ();
 
public static void AccessCache ()
{
    lock ( htCache.SyncRoot )


C#线程安全 C#中,线程安全是一个非常重要的概念,下面我们将深入了解线程安全的概念、常见的线程安全问题以及一些解决方法。线程安全的类型(Thread-Safe Types):线程安全的类型是指可以安全地访问和修改数据的类型。通过使用互斥锁、原子操作、并发集合和线程安全的类型等方法,我们可以解决常见的线程安全问题,保证程序的正确性、可靠性和稳定性。线程安全是一个非常重要的概念,它指的是当多个线程并发访问共享资源时,程序仍然能够保持正确性、可靠性和稳定性的能力。通过使用原子操作,我们可以避免竞争条件的问题。 阅读详情

相关推荐

WinCC与施耐德PLC的Modbus TCP通信实战指南

本文详细解析了西门子WinCC与施耐德PLC通过Modbus TCP协议实现通信的完整实战流程。内容涵盖硬件网络配置、Modbus地址映射核心规则、WinCC驱动连接建立、变量创建及高级数据访问技巧,并提供了常见故障排查思路,帮助工程师高效解决跨品牌设备通信难题。

weixin_29194693的博客 874

C#线程安全的集合

System.Collections.Concurrent 命名空间 提供多个线程安全集合类,只要多个线程同时访问集合,就应使用这些类来代替System.Collections 和 System.Collections.Generic 命名空间中的相应类型。 但是,不保证通过扩展方法或通过显式接口实现访问集合对象是线程安全的,可能需要由调用方进行同步。 类 类说明BlockingCollection<T>为实现 IProducerConsumerCollection<T> 的线程安

欢迎指错||纠正||建议||水评+点赞&&评论&&关注&&转发 5001

C#实现线程安全List容器(附完整源码)

C#实现线程安全List容器(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 757

【原创】C#List线程安全相关解决方案

在多线程程序中,可能多个线程都需要操作一个共用的List,而微软实现的List却是非线程安全的。也就是说,在多线程情况下,共用的List会产生线程安全问题。 对于这个问题,有不少的解决方案,比如换用线程安全的类型,如: BlockingCollection<T> ConcurrentBag<T> ConcurrentDictionary<TKey,TValue> ConcurrentQueue<T> ConcurrentStack<T>

赵庆明老师 1万+

什么是线程安全?Vector是一个线程安全类吗?_vector 是一 个线程安全类吗

为了解决这个问题,Java提供了一些线程安全集合类,如Vector的线程安全版本Vector的线程安全版本,或者使用包中的类,如等。这些类在内部通过使用锁或其他同步机制来确保线程安全。因此,虽然Vector类本身不是线程安全的,但是你可以使用Java提供的一些线程安全集合类来确保在多线程环境中使用Vector的安全性。在使用这些集合类时,你需要确保正确地管理锁或其他同步机制,以避免死锁或其他同步问题。

639

C# 单例模式整理

参考: 《Implementing the Singleton Pattern in C#》 《CLR via C# (第4版)》 方式1. 非线程安全   public class Singleton { private static Singleton instance = null; private Singleton() { } pub

一个被IT搞的 344

C# 的常见误区整理

本文为《编写高质量的代码:改善C#程序的157个建议》中整理出来对我有用的知识,算是读书笔记吧。所以并不全,有兴趣了解详情的童鞋可以去买纸质书一探究竟。

阿修罗道 1700

C# event线程安全

突然想到有关C#中使用event特性时关于线程安全的问题,以前虽然有遵从“复制引用+null判断”的模式(盲目地),但没有深入了解和思考。 为之查询了资料和实验,对此有了进一步的理解。 一般event使用模式 定义(field-like event): public event EventHandler Done; 类内raise: protected void On...

weixin_30650039的博客 265

C# 多线程 & 异步编程知识体系整理

C# 多线程与异步编程知识体系涵盖了线程基础、线程操作、异步编程核心、同步机制、常见误区及扩展工具。线程是操作系统独立运行的最小单位,多线程适用于批量任务并行处理和多任务独立运行。线程池通过预创建线程减少开销,线程安全通过同步机制和原子操作实现。线程操作包括创建、终止、挂起/恢复,并发控制工具包括传统锁机制和轻量级同步。异步编程核心包括Task模型、方法声明、异步控制流(取消机制、超时处理)。同步机制与并发控制涉及线程安全组件和异步锁策略。常见误区包括异步不等于多线程、Task.Result不总会阻塞、aw

qq_36298692的博客 743

C# 规范整理 集合和Linq

转自:天空的湛蓝cnblogs.com/zhan520g/p/11018163.htmlLINQ(Language Integrated Query,语言集成查询)提供了类似于SQL的语法,能对集合进行遍历、筛选和投影。一旦掌握了LINQ,你就会发现在开发中再也离不开它。前言C#中的集合表现为数组和若干集合类。不管是数组还是集合类,它们都有各自的优缺点。如何使用好集合是我们在开发过程中必须掌握的技...

weixin_68436326的博客 183

C# 多线程(菜鸟教程及爱整理)

C# 多线程--菜鸟教程 线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。 线程生命周期 线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。 下面列出了线程生命周期中的各种状态: 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。 就绪状态:当线程准备好运行并等待 CPU 周期时的状况。 不可运行状态:

weixin_45582785的博客 1万+

C# 多线程&异步编程 笔记整理

C# 中的多线程和异步编程是提高应用程序性能和响应能力的关键技术。多线程允许程序同时执行多个任务,适用于 CPU 密集型操作,而异步编程则通过不阻塞线程来提高效率,特别适合 IO 密集型任务。线程池的使用可以减少线程创建和销毁的开销,而线程安全则通过同步机制和原子操作来确保数据一致性。异步编程通过 async 和 await 关键字实现,允许任务在后台执行而不阻塞主线程。任务取消和超时处理是异步编程中的重要概念,而同步上下文则帮助管理线程切换。理解这些概念和工具对于编写高效、可靠的 C# 应用程序至关重要。

qq_36298692的博客 771

C#关于List线程安全问题()

C# 关于List线程安全问题 首先,List这个集合类是不安全的。 直接给例子,讲一下吧: using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace vscode_test2 { class Program { ...

皮皮君的博客 4620

C# list线程安全

C# list线程安全

qq_36437991的博客 1243

C# 线程安全的集合

GetOrAdd:如果键不存在,方法会向容器中添加新的键和值,如果存在则返回现有的值,并不添加新值。AddOrUpdate:如果键不存在,方法会在容器中添加新的键和值,如果存在,则更新现有的键和值。CompleteAdding:告诉容器,添加元素完成。对于读操作是完全无锁的,当很多线程要修改数据时,它会使用细粒度的锁。一个无序的集合,程序可以向其中插入元素,或删除元素。TryPeek:从集合中取出元素,但不删除该元素。TryPeek:从容器中取出元素,但不删除。TryAdd:尝试在容器中添加新的键和值。

一个编程的同学 1407

c# 高级编程-多线程安全

线程安全介绍

qq_45165610的博客 2672

C# 系统的集合类型与线程安全集合类

C# 系统的集合类型与线程安全集合类

weixin_39604653的博客 1836

C# 4.0 之线程安全集合篇

作为多线程和并行计算不得不考虑的问题就是临界资源的访问问题,解决临界资源的访问通常是加锁或者是使用信号量,这个大家应该很熟悉了。   而集合作为一种重要的临界资源,通用性更广,为了让大家更安全的使用它们,微软为我们带来了强大的并行集合:System.Collections.Concurrent里面的各位仁兄们。   首先,咱们从一个经典的问题谈起。 生产者消费者问题   这

lxrj2008的专栏 1189

Qt c++ llama.cpp 运行 LLaMA-3-8B 中文对话机器人(多模态输入)

助手:

寒寒豆豆软件公司的博客 172

法兰盘零件的加工工艺,设计钻4-Φ7孔的钻床夹具.zip

法兰盘零件的加工工艺,设计钻4-Φ7孔的钻床夹具.zip

上一篇: C#多线程处理多个队列的数据
下一篇: 在 System.FormatException 中第一次偶然出现的“mscorlib.dll”类型的异常,如何调试呢。
鹧鸪菜
博客等级 码龄20年 330粉丝 115原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值