C#在多线程中使用dictionary时的安全问题

问题出现的情景:在计算一个特征集中所有特征与一个数据集的所有实例之间的所有组合距离时,采用多线程的方法来提高计算速度。如下,CalculateDistanceThread是计算一个特征与数据集中所有实例的距离,并将其距离加入到tmp_dist_matrix的字典中。



采用上述方法,导致的问题在多线程往tmp_dist_matrix字典中添加内容的时候,导致多个线程可能同时对tmp_dist_matrix进行操作,从而导致tmp_dist_matrix的不一致性。

一般出现如下几种情况可能导致线程不安全:

1.争抢独占资源,如同时写一个文件,对独占资源的排它锁。

2.程序中的全局对象,如类中对静态成员,被多个线程同时更新(修改或者删除),会产生脏的数据或者是其它线程得不到正确的数据的后果;同一个类实例的非静态成员,被此实例中的函数使用(其中有修改实例成员的功能),此时两个以上线程同时使用时,就可能使类成员的状态对同一个线程的使用的不一致性。

解决线程安全问题的两种方法:

1.把这个对象或者资源锁住,即等我用完了,其它线程才能再能,否则其它线程等待,这也是最常见的方式。

2.给每个线程分配属于只属于自己的"全局对象"(这样听起来好像有点别扭)。

第一种方法:用Lock(C#)

1.锁住同一个实例中的全局资源(同一实例中非静态成员) 

复制代码
public   void  Function()
{
    System.Object lockThis 
=   new  System.Object();
    
lock (lockThis)
    {
        
//  Access thread-sensitive resources.
    }
}

在本例中,改后的代码如下:

 private static Dictionary<MTS_Feature_Pair, double> tmp_dist_matrix { get; set; }
        private static readonly object locker = new object();
        private class CalculateDistantThread
        {
            private Feature feature;
            private DataSet dataset;
            public CalculateDistantThread(Feature f, DataSet ds)
            { feature = f; dataset = ds; }
            public void CalculateDistance()
            {
                foreach (MTS mts in dataset.dataset)
                {
                    MTS_Feature_Pair mfp = new MTS_Feature_Pair(mts, feature);
                    Distance dist = new Distance(mts.mts[feature.variable_index], feature, Distance.SimilarityMeasureMethod.Euclidean);
                    lock (locker)
                    {
                        tmp_dist_matrix.Add(mfp, dist.value);
                    }

                }
            }
        }

2.锁住类中静态成员 

复制代码
static  StringBuilder CstrBuild  =   new  StringBuilder();
static  System.Object lockThis  =   new  System.Object();
public   void  Function()
{    
    
lock (lockThis)
    {
        CstrBuild.Append(
" test " );
    }
}

转载:http://www.cnblogs.com/luyinghuai/articles/1279665.html

第二,使用给每一个线程分配自己的“全局对象”的方法,实现一个带有缓存的写文件功能的线程安全的类(避免每条数据写一次,造成IO瓶颈)。 

它的原则是给每个线程分配自己的“全局对象”。

实现功能:

当每个线程的StringBuilder对象,到达一定长度 const int CBufferLenght = 10000*38;  后才写入文件。 

步骤是:

1, 声明一个分局对象集合,我用了Dictionary对象,key是线程id,value就是它的“全局对象”

static  Dictionary < int , StringBuilder >  CLBufferOfEThread  =   new  Dictionary < int , StringBuilder > ();

 

2,需要注意的是,当第一次将数据加入到全局集合对象Dictionary时,需要锁定Dictionary对象

lock  (lockBuffer)
 {
  CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId] 
=   new  StringBuilder(pstrContent);
}

 

 

3, 给Dictionary对象赋值时,无需使用lock(因为它是线程独占的全局对象,不会冲突,我给每个线程id分配了自己的“全局对象”,即集合中的某个元素)

CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId].AppendLine(pstrContent);

 

4,在虚构函数处理还未写入文件的数据

复制代码
~ StreamWriteWithBuffer()
        {
            
foreach  (StringBuilder valus  in  CLBufferOfEThread.Values)
            {
                
if  (valus.Length  >   0   &&  CLimpIDfileName.Length  !=   0 )
                {
                    
using  (StreamWriter logWrite  =   new  StreamWriter(CLimpIDfileName,  true , Encoding.Default))
                    {
                        logWrite.WriteLine(valus.ToString());
                    }
                    valus.Remove(
0 , valus.Length);
                }
            }
        }
复制代码



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C#提供了两种线安全的字典类:Dictionary和ConcurrentDictionary[^1]。下面是它们的介绍和使用方法: 1. DictionaryDictionaryC#中最常用的字典类,但它不是线安全的。如果在多线程环境下使用Dictionary,可能会导致数据竞争和不一致的结果。为了解决这个问题,可以使用锁机制来保护Dictionary的访问。例如,在将数据添加到全局Dictionary,可以使用lock关键字锁定Dictionary对象,以确保线安全[^2]。下面是一个示例代码: ```csharp private static Dictionary<int, string> dict = new Dictionary<int, string>(); private static object lockObj = new object(); public void AddToDictionary(int key, string value) { lock (lockObj) { dict[key] = value; } } ``` 2. ConcurrentDictionary: ConcurrentDictionaryC#提供的线安全字典类,它可以在多线程环境下安全地进行并发访问。ConcurrentDictionary使用了一种称为锁分段技术的方法,它将字典分成多个段,每个段都有自己的锁,这样可以提高并发性能。使用ConcurrentDictionary,不需要手动添加锁来保护访问。下面是一个示例代码: ```csharp private static ConcurrentDictionary<int, string> dict = new ConcurrentDictionary<int, string>(); public void AddToConcurrentDictionary(int key, string value) { dict[key] = value; } ``` 需要注意的是,虽然ConcurrentDictionary线安全的,但在某些特定的场景下,仍然需要使用额外的同步机制来保证数据的一致性。例如,如果需要对字典中的多个键值对进行原子操作,可以使用ConcurrentDictionary的TryUpdate方法来保证操作的原子性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值