2015-10-22 39 views
0

我正在爲通過正則表達式解析字典和不適用於DateTime元素的列表編寫一些代碼。下面的代碼可以工作,而且工作得很好。但是,當我回頭看到這個Parallel.ForEach是我寫的。我的問題很簡單,如果我鎖定線程,使用Parallel.ForEach會有性能提升嗎?我覺得這似乎擊敗了整個目的。鎖定到單線程的異步編程和並行編程的性能

此外,我覺得好像同樣的想法/問題適用於異步情況,因爲我鎖定了線程。

namespace StackOverflowExample 
{ 
    public class ScheduledTimeList : List<ScheduledTime> 
    { 
     public static ScheduledTimeList Parse(
      Dictionary<string, string> dateTimeStrings, string startRegex, 
      string endRegex, object sync) 
     { 
      var scheduledTimes = new ScheduledTimeList(); 

      Parallel.ForEach(from dt in dateTimeStrings.AsParallel() 
          select ParseScheduledTime(dt, startRegex, endRegex), dt => 
      { 
       lock (sync) { scheduledTimes.Add(dt); } 
      }); 

      return scheduledTimes; 
     } 

     private static ScheduledTime ParseScheduledTime(
      KeyValuePair<string, string> dateTime, string startRegex, 
      string endRegex) 
     { 
      var startString = 
       dateTime.Key + " " + new Regex(startRegex).Match(dateTime.Value); 
      var endString = 
       dateTime.Key + " " + new Regex(endRegex).Match(dateTime.Value); 

      var startDateTime = DateTime.Parse(startString); 
      var endDateTime = DateTime.Parse(endString); 

      return new ScheduledTime(startDateTime, 
       endDateTime); 
     } 

     private static async Task<ScheduledTime> ParseScheduledTimeAsync(
      KeyValuePair<string, string> dateTime, string startRegex, 
      string endRegex) 
     { 
      var startString = Task.Run(() => 
       dateTime.Key + " " + new Regex(startRegex).Match(dateTime.Value).Value); 
      var endString = Task.Run(() => 
       dateTime.Key + " " + new Regex(endRegex).Match(dateTime.Value).Value); 
      await Task.WhenAll(startString, endString); 

      var startDateTime = await Task.Run(() => 
       DateTime.Parse(startString.Result)); 
      var endDateTime = await Task.Run(() => 
       DateTime.Parse(endString.Result)); 

      return new ScheduledTime(startDateTime, 
       endDateTime); 
     } 
    } 
} 
+1

代碼中沒有任何內容似乎是CPU密集型的。我會認爲這個代碼的性能會更差,試圖平行進行。你是否使用單線程來嘗試代碼? – Enigmativity

+0

「是否有性能增益」 - 你的性能測量告訴你什麼? –

回答

2

以下是一些誤解,分析和錯誤決定的問題。首先,如果你開始類似於Parallel.ForEach或者AsParallel或者Tasks, 幾乎相同,都運行在TPL之上,那麼就不會有一個線程,而是多個或者一個線程,這都取決於負載平衡,但我的意思是,該鎖並不能確保所有部分將在同一個線程上運行。相反,鎖是確保在任何給定的時間內,只有一個線程將在鎖定區域內運行。當然不是同一件事

現在第二個問題是,你讓平常的初學者犯了錯誤。引入太多的併發並沒有幫助。當然,我在Parse方法中看到一個問題,如果AsParallel(),PLINQ方法在那裏首先啓動內部並行塊以獲得結果,那麼它將完全並行地工作,但是隨後您返回IEnumerable到Parallel.ForEach,迫使PLINQ同步返回到IEnumerable。這是糟糕的表現決定。

第三個錯誤是在併發代碼中使用List等舊集合。併發代碼在併發集合上玩得最好。 這裏意味着使用ConcurrentBag代替List。

public class ScheduledTimeList : ConcurrentBag<ScheduledTime> 

那麼到底它可能看起來像這樣在Parse方法:

var scheduledTimes = new ScheduledTimeList(); 

dateTimeStrings 
.AsParallel() 
.ForAll(dts => 
{ 
    var dt = ParseScheduledTime(dts, startRegex, endRegex); 
    scheduledTimes.Add(dt);//add's items concurently into bag, 
}); 

並行/異步/並行編程是堅硬的東西,它真的需要大量的研究甚至超越任務,類並行,PLINQ和異步/等待。

請把我的建議作爲進一步學習的指針,放輕鬆,一步一步,我不想讓它灰心,或者聽起來好像不可能理解這一點,它只是一個更廣泛的問題。

+0

周到的回答。 –

1

有在使用Parallel.ForEach如果我鎖定線程性能提升?

恰恰相反。由於整個代碼塊是由鎖語句控制的,因此您會產生開銷並行運行,然後立即導致所有線程以串行方式運行。