问题出现的情景:在计算一个特征集中所有特征与一个数据集的所有实例之间的所有组合距离时,采用多线程的方法来提高计算速度。如下,CalculateDistanceThread是计算一个特征与数据集中所有实例的距离,并将其距离加入到tmp_dist_matrix的字典中。
采用上述方法,导致的问题在多线程往tmp_dist_matrix字典中添加内容的时候,导致多个线程可能同时对tmp_dist_matrix进行操作,从而导致tmp_dist_matrix的不一致性。
一般出现如下几种情况可能导致线程不安全:
1.争抢独占资源,如同时写一个文件,对独占资源的排它锁。
2.程序中的全局对象,如类中对静态成员,被多个线程同时更新(修改或者删除),会产生脏的数据或者是其它线程得不到正确的数据的后果;同一个类实例的非静态成员,被此实例中的函数使用(其中有修改实例成员的功能),此时两个以上线程同时使用时,就可能使类成员的状态对同一个线程的使用的不一致性。
解决线程安全问题的两种方法:
1.把这个对象或者资源锁住,即等我用完了,其它线程才能再能,否则其它线程等待,这也是最常见的方式。
2.给每个线程分配属于只属于自己的"全局对象"(这样听起来好像有点别扭)。
第一种方法:用Lock(C#)
1.锁住同一个实例中的全局资源(同一实例中非静态成员)
{
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 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就是它的“全局对象”
2,需要注意的是,当第一次将数据加入到全局集合对象Dictionary时,需要锁定Dictionary对象
{
CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId] = new StringBuilder(pstrContent);
}
3, 给Dictionary对象赋值时,无需使用lock(因为它是线程独占的全局对象,不会冲突,我给每个线程id分配了自己的“全局对象”,即集合中的某个元素)
4,在虚构函数处理还未写入文件的数据
{
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);
}
}
}