3

我想使下面的代码是线程安全的。不幸的是,我试图在这个代码中的各个级别锁定,但没有成功。我看起来能够实现线程安全的唯一实例是在整个循环中放置一个锁,使得Parallel.ForEach不会比使用foreach更快(可能甚至更慢)。该代码相对/几乎安全,没有锁定。它似乎在每20次左右执行一次时,geneTokens.Value [-1]键和gtCandidates.Value [-1]键的总和略有变化。更好的方法让我的Parallel.ForEach线程安全吗?

我意识到字典不是线程安全的。但是,我无法将这个特定对象改为ConcurrentDictionary,而不会影响下游的主要性能。我宁愿用常规的foreach来运行这部分代码,而不是改变那个特定的对象。但是,我正在使用ConcurrentDictionary来保存各个Dictionary对象。我也尝试过这个改变,但它并没有解决我的种族问题。

这里是我的类级变量:

//Holds all tokens derived from each sequence chunk 
public static ConcurrentBag<sequenceItem> tokenBag = 
    new ConcurrentBag<sequenceItem>(); 
public BlockingCollection<sequenceItem> sequenceTokens = new 
    BlockingCollection<sequenceItem>(tokenBag); 
public ConcurrentDictionary<string, int> categories = new 
    ConcurrentDictionary<string, int>(); 
public ConcurrentDictionary<int, Dictionary<int, int>> gtStartingFrequencies = new 
    ConcurrentDictionary<int, Dictionary<int, int>>(); 
public ConcurrentDictionary<string, Dictionary<int, int>> gtCandidates = new 
    ConcurrentDictionary<string, Dictionary<int, int>>(); 
public ConcurrentDictionary<string, Dictionary<int, int>> geneTokens = new 
    ConcurrentDictionary<string, Dictionary<int, int>>(); 

这里是Parallel.ForEach:

Parallel.ForEach(sequenceTokens.GetConsumingEnumerable(), seqToken => 
{ 
    lock (locker) 
    { 
    //Check to see if the Sequence Token is a Gene Token 
    Dictionary<int, int> geneTokenFreqs; 
    if (geneTokens.TryGetValue(seqToken.text, out geneTokenFreqs)) 
    { //The Sequence Token is a Gene Token 


     *****************Race Issue Seems To Occur Here**************************** 
     //Increment or create category frequencies for each category provided 
     int frequency; 
     foreach (int category in seqToken.categories) 
     { 
     if (geneTokenFreqs.TryGetValue(category, out frequency)) 
     { //increment the category frequency, if it already exists 
      frequency++; 
      geneTokenFreqs[category] = frequency; 
     } 
     else 
     { //Create the category frequency, if it does not exist 
      geneTokenFreqs.Add(category, 1); 
     } 
     } 

     //Update the frequencies total [-1] by the total # of categories incremented. 
     geneTokenFreqs[-1] += seqToken.categories.Length; 
     ****************************************************************************** 
    } 
    else 
    { //The Sequence Token is NOT yet a Gene Token 
     //Check to see if the Sequence Token is a Gene Token Candidate yet 
     Dictionary<int, int> candidateTokenFreqs; 
     if (gtCandidates.TryGetValue(seqToken.text, out candidateTokenFreqs)) 
     { 
     *****************Race Issue Seems To Occur Here**************************** 
     //Increment or create category frequencies for each category provided 
     int frequency; 
     foreach (int category in seqToken.categories) 
     { 
      if (candidateTokenFreqs.TryGetValue(category, out frequency)) 
      { //increment the category frequency, if it already exists 
      frequency++; 
      candidateTokenFreqs[category] = frequency; 
      } 
      else 
      { //Create the category frequency, if it does not exist 
      candidateTokenFreqs.Add(category, 1); 
      } 
     } 

     //Update the frequencies total [-1] by the total # of categories incremented. 
     candidateTokenFreqs[-1] += seqToken.categories.Length; 
     ***************************************************************************** 

     //Only update the candidate sequence count once per sequence 
     if (candidateTokenFreqs[-3] != seqToken.sequenceId) 
     { 
      candidateTokenFreqs[-3] = seqToken.sequenceId; 
      candidateTokenFreqs[-2]++; 

      //Promote the Token Candidate to a Gene Token, if it has been found >= 
      //the user defined candidateThreshold 
      if (candidateTokenFreqs[-2] >= candidateThreshold) 
      { 
      Dictionary<int, int> deletedCandidate; 
      gtCandidates.TryRemove(seqToken.text, out deletedCandidate); 
      geneTokens.TryAdd(seqToken.text, candidateTokenFreqs); 
      } 
     } 
     } 
     else 
     { 
     //create a new token candidate frequencies dictionary by making 
     //a copy of the default dictionary from 
     gtCandidates.TryAdd(seqToken.text, new 
      Dictionary<int, int>(gtStartingFrequencies[seqToken.sequenceId])); 
     } 
    } 
    } 
}); 
+0

有这个代码其他奇怪的事情:它是如何让你增加'频率“没有首先初始化? – Tudor

+0

工作得很好,因为频率被用作geneTokenFreqs.TryGetValue()的“out”变量。唯一增加的时间是如果变量存在并从TryGetValue返回...我向你保证代码执行。我一直在运行它整晚:) –

+0

对不起,我没有看到'out'部分。然后就可以了。 – Tudor

回答

0

显然一个数据竞争来自一个事实,即某些线程将增加items here:

geneTokens.TryAdd(seqToken.text, candidateTokenFreqs); 

和其他人将被读取的位置:

if (geneTokens.TryGetValue(seqToken.text, out geneTokenFreqs)) 
+0

你是对的。我从上面删除了它们。在我发布之前,他们实际上是在之前的测试中发现的。我以前曾尝试过使用geneTokens.Contains(),并且没有成功尝试使用geneTokenFreqs = geneTokens [seqToken.text];这种方法并没有解决我的问题。 –

+0

@Jake Drew:我已经对我认为的问题进行了编辑。你可以尝试只锁定代码的这些部分。 – Tudor

+0

我确定如何有效地锁定TryAdd,而不用在整个块周围包裹锁,这会导致我回到原始问题。我尝试了以下模式,但没有运气:lock(locker) {isGeneToken = geneTokens.TryGetValue(seqToken.text,out geneTokenFreqs); } if(isGeneToken) –

-1

如何我已经在我的项目中使用并发词典:

余米放一个标志字典,并从另一个线程检查,如果标志是有或不。如果存在旗我做我的任务相应..

对于这个什么IM做的是:

1)声明并发字典 2)添加使用TryADD方法 3)试图检索使用TryGet扁旗Methid。

1)声明

Dim cd As ConcurrentDictionary(Of Integer, [String]) = New ConcurrentDictionary(Of Integer, String)() 

2)添加

If cd.TryAdd(1, "uno") Then 
     Console.WriteLine("CD.TryAdd() succeeded when it should have failed") 
     numFailures += 1 
    End If 

3)检索

If cd.TryGetValue(1, "uno") Then 
     Console.WriteLine("CD.TryAdd() succeeded when it should have failed") 
     numFailures += 1 
    End If