为什么执行新线程而不创建新线程?

时间:2019-08-26 13:54:34

标签: c# multithreading thread-safety

偶尔,会有另一个线程开始执行我的代码。

// the main method:
// ...
   MatchResultsForRules(rules, results);
   ApplyRules(rules);
//...

public void MatchResultsForRules(List<Rule> Rules, List<SearchResult> Results)
{
   foreach (Rule rule in Rules)
   {
      foreach (SearchResult res in Results)
      {
         if (isResultMatchRule(rule, res))
         {
            rule.searchResults.Add(res);
         }
      }
   }
}

public void ApplyRules (List<Rule> Rules)
{
   foreach (Rule rule in Rules)
   {
      foreach(SearchResult res in rule.searchResults)
      {
         ApplyRule(rule, res);
      }
   }
}

我知道还有另一个线程,因为一旦看到问题发生(计算不匹配),我就打印了详细的日志,其中包括每个动作的线程ID,并且按照动作的顺序看到了混乱,当然还有两个不同的线程ID。

我通过操纵搜索结果本身而不是操纵每个规则的搜索结果列表(在下面说明)解决了该问题。

解决方法:

public void ApplyRules (List<Rule> rules, List<SearchResult> searchResults)
{
   foreach (Rule rule in rules)
   {
      foreach(SearchResult resFromRule in rule.searchResults)
      {
         SearchResult res = searchResults.First(
            r => r.Id.Equals(resFromRule.Id)
         );
         ApplyRule(rule, res);
      }
   }
}

我只是想更好地理解这个问题,以便将来不再重复此错误。

1 个答案:

答案 0 :(得分:0)

此答案基于以下假设:rulesresults是共享或单例实例,并且以下代码被并行调用:

MatchResultsForRules(rules, results);
ApplyRules(rules);

MatchResultsForRules()通过searchResults编辑rule.searchResults.Add(res)。这样一来,您在ApplyRules()上通过foreach(SearchResult res in rule.searchResults)遍历结果之前可能会有短暂的时间间隔。在这段时间内,另一个线程可能正在执行MatchResultsForRules()并编辑searchResults。更重要的是:如果一个线程尝试在另一个迭代过程中同时编辑searchResults,那么您将以Exception结尾。

为避免此类错误

  1. 询问是否需要存储searchResults以便稍后进行迭代。我知道,可能有其他原因或代码部分可能需要存储结果,而不是直接rule.searchResults.Add(res);来调用ApplyRule(rule, res);
  2. 考虑是否不能使用共享实例,而是为每个线程提供自己的实例。
  3. 使用lock关键字来确保这部分代码不会并行执行。这种方法可能会大大降低您的运行时性能。
lock(yourSharedReadonlyLockObject)
{
   MatchResultsForRules(rules, results);
   ApplyRules(rules);
}