• [.NET]ConcurrentDictionary 线程安全的集合类


      ConcurrentDictionary 是.NET 4.0中新添加的,相信其性能是比自己手动对Dictionary加锁要好得多

       其中大部分方法是保证线程安全的:

       TryAdd()

    TryUpdate()

    TryRemove()

    AddOrUpdate()

    GetOrAdd()



    其中有些地方要注意的:

    1.作为GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)参数的委托 ,不保证里面代码的线程安全,也不保证委托执行的次数

    当要获取的key的value不存在的时候,就会执行委托返回一个新的value值并添加都集合里面去

      但是,由于委托并不在ConcurrentDictionary 的锁里面,所以委托里面的代码不是线程安全的,而且不保证委托只被执行一次。

    所以要保证委托总是返回确定的值,或者用Lazy<T>代替委托

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Collections.Concurrent;
    using System.Threading.Tasks;

    namespace ConsoleApplication2
    {
    class Program
    {
    static ManualResetEvent Signal = new ManualResetEvent(false);//用于同步线程
    static int Count = 0;
    static ConcurrentDictionary<int, int> MyDictionary = new ConcurrentDictionary<int, int>();
    public static void TaskAction()
    {
    Signal.WaitOne();


    MyDictionary.GetOrAdd(1, key =>
    {

    Interlocked.Increment(ref Count);//以原子操作的形式递增指定变量的值并存储结果
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());
    return 1;
    });
    }
    delegate void myDel();
    static void Main(string[] args)
    {
    myDel del = TaskAction;




    List<IAsyncResult> results = new List<IAsyncResult>();

    for (int i = 0; i < 4; i++)
    {
    results.Add(del.BeginInvoke(null, null));
    }
    Thread.Sleep(3000);//留足够的时间让线程池准备好线程
    Signal.Set();

    results.ForEach(item => item.AsyncWaitHandle.WaitOne());




    Console.WriteLine("执行次数" + Count.ToString());

    Console.ReadKey();
    }

    }
    }


    上面代码执行的结果可能与原本的结果1是不一致的。

    如果采用.NET4.0的Task来试验的话,要同时开几十个线程才偶尔出现错误值,可见Task的优化程度。

    2.关于ConcurrentDictionary 的迭代

    如果以下面的形式进行迭代,迭代的时候不会阻塞线程,但是会读到脏数据

               foreach (var item in MyDictionary)
    {
    //其他代码
    }

    如果下面这种形式,迭代的结果将是MyDictionary某一时刻的快照,不包含脏数据

               foreach (var item in MyDictionary.ToArray())
    {
    //其他代码
    }

    同时,ConcurrentDictionary 的Count, Keys, Values 属性也是某一时刻的快照

    3.对ConcurrentDictionary 的某个Value的引用的读写不是线程安全的

    下面执行的结果不是可预料的:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Collections.Concurrent;
    namespace ConsoleApplication2
    {
    class Program
    {
    public class myclass
    {
    public int value = 0;
    }
    public static ConcurrentDictionary<int, myclass> MyDictionary = new ConcurrentDictionary<int, myclass>();

    static void Main(string[] args)
    {

    myclass myclass = new Program.myclass();
    myclass.value = 0;
    MyDictionary.TryAdd(1, myclass);
    Thread xxx = new Thread(() =>
    {
    for (int i = 0; i < 100000000; i++)
    {
    myclass value;
    if (MyDictionary.TryGetValue(1, out value))
    {
    value.value++;

    }


    }
    });
    Thread vvv = new Thread(() =>
    {
    for (int i = 0; i < 100000000; i++)
    {

    myclass value;
    if (MyDictionary.TryGetValue(1, out value))
    {
    value.value--;
    }
    }
    });
    xxx.Start();
    vvv.Start();

    xxx.Join();
    vvv.Join();
    Console.WriteLine(myclass.value);
    }
    }
    }







       

  • 相关阅读:
    【BZOJ4448】【SCOI2015】情报传递
    【BZOJ2006】【NOI2010】超级钢琴
    NOIp2018模拟赛四十五~??
    【BZOJ4940】【YNOI2016】这是我自己的发明
    数据迁移—datax
    DG模拟GAP手动处理
    DG问题:ORA-16416: No viable Physical Standby switchover targets available
    管理和维护DG
    DG问题
    DG概念与机制
  • 原文地址:https://www.cnblogs.com/lihuixian001/p/2355015.html
Copyright © 2020-2023  润新知