2013-01-24 74 views
2

假設我的C#程序中有任意數量的線程。每個線程都需要通過查找歷史記錄來查找特定路徑的變更集ID。該方法看起來像這樣:單獨的線程是否需要它自己的VersionControlServer實例與另一個線程同時工作?

public List<int> GetIdsFromHistory(string path, VersionControlServer tfsClient) 
{ 
    IEnumerable submissions = tfsClient.QueryHistory(
     path, 
     VersionSpec.Latest, 
     0, 
     RecursionType.None, // Assume that the path is to a file, not a directory 
     null, 
     null, 
     null, 
     Int32.MaxValue, 
     false, 
     false); 

    List<int> ids = new List<int>(); 
    foreach(Changeset cs in submissions) 
    { 
     ids.Add(cs.ChangesetId); 
    } 
    return ids; 
} 

我的問題是,是否每個線程需要它自己的VersionControlServer實例或將一個就夠了?我的直覺告訴我,每個線程都需要自己的實例,因爲TFS SDK使用webservices,如果我真的想要獲得並行行爲,我應該打開多個連接。如果我只使用一個連接,我的直覺告訴我,即使我有多個線程,我也會得到序列行爲。

如果我需要與線程一樣多的實例,我想使用Object-Pool模式,但是如果不使用,連接會超時並關閉很長時間嗎?文檔在這方面似乎很少。

回答

1

看來使用SAME客戶端的線程是最快的選擇。

這裏是測試程序的輸出,它運行4次測試,每次測試5次,並以毫秒爲單位返回平均結果。顯然,使用多個線程在同一客戶端是最快的執行:

Parallel Pre-Alloc: Execution Time Average (ms): 1921.26044 
Parallel AllocOnDemand: Execution Time Average (ms): 1391.665 
Parallel-SameClient: Execution Time Average (ms): 400.5484 
Serial: Execution Time Average (ms): 1472.76138 

僅供參考,這裏的測試程序本身(也GitHub):

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Microsoft.TeamFoundation; 
using Microsoft.TeamFoundation.Client; 
using Microsoft.TeamFoundation.VersionControl.Client; 
using System.Collections; 
using System.Threading.Tasks; 
using System.Diagnostics; 

namespace QueryHistoryPerformanceTesting 
{ 
    class Program 
    { 
     static string TFS_COLLECTION = /* TFS COLLECTION URL */ 
     static VersionControlServer GetTfsClient() 
     { 
      var projectCollectionUri = new Uri(TFS_COLLECTION); 
      var projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(projectCollectionUri, new UICredentialsProvider()); 
      projectCollection.EnsureAuthenticated(); 
      return projectCollection.GetService<VersionControlServer>(); 
     } 

     struct ThrArg 
     { 
      public VersionControlServer tfc { get; set; } 
      public string path { get; set; } 
     } 

     static List<string> PATHS = new List<string> { 
      // ASSUME 21 FILE PATHS 
     }; 

     static int NUM_RUNS = 5; 
     static void Main(string[] args) 
     { 
      var results = new List<TimeSpan>(); 

      for (int i = NUM_RUNS; i > 0; i--) 
      { 
       results.Add(RunTestParallelPreAlloc()); 
      } 
      Console.WriteLine("Parallel Pre-Alloc: Execution Time Average (ms): " + results.Select(t => t.TotalMilliseconds).Average()); 

      results.Clear(); 
      for (int i = NUM_RUNS; i > 0; i--) 
      { 
       results.Add(RunTestParallelAllocOnDemand()); 
      } 
      Console.WriteLine("Parallel AllocOnDemand: Execution Time Average (ms): " + results.Select(t => t.TotalMilliseconds).Average()); 

      results.Clear(); 
      for (int i = NUM_RUNS; i > 0; i--) 
      { 
       results.Add(RunTestParallelSameClient()); 
      } 
      Console.WriteLine("Parallel-SameClient: Execution Time Average (ms): " + results.Select(t => t.TotalMilliseconds).Average()); 

      results.Clear(); 
      for (int i = NUM_RUNS; i > 0; i--) 
      { 
       results.Add(RunTestSerial()); 
      } 
      Console.WriteLine("Serial: Execution Time Average (ms): " + results.Select(t => t.TotalMilliseconds).Average()); 
     } 

     static TimeSpan RunTestParallelPreAlloc() 
     { 
      var paths = new List<ThrArg>(); 
      paths.AddRange(PATHS.Select(x => new ThrArg { path = x, tfc = GetTfsClient() })); 
      return RunTestParallel(paths); 
     } 

     static TimeSpan RunTestParallelAllocOnDemand() 
     { 
      var paths = new List<ThrArg>(); 
      paths.AddRange(PATHS.Select(x => new ThrArg { path = x, tfc = null })); 
      return RunTestParallel(paths); 
     } 

     static TimeSpan RunTestParallelSameClient() 
     { 
      var paths = new List<ThrArg>(); 
      var _tfc = GetTfsClient(); 
      paths.AddRange(PATHS.Select(x => new ThrArg { path = x, tfc = _tfc })); 
      return RunTestParallel(paths); 
     } 

     static TimeSpan RunTestParallel(List<ThrArg> args) 
     { 
      var allIds = new List<int>(); 

      var stopWatch = new Stopwatch(); 
      stopWatch.Start(); 
      Parallel.ForEach(args, s => 
      { 
       allIds.AddRange(GetIdsFromHistory(s.path, s.tfc)); 
      } 
      ); 
      stopWatch.Stop(); 

      return stopWatch.Elapsed; 
     } 

     static TimeSpan RunTestSerial() 
     { 
      var allIds = new List<int>(); 
      VersionControlServer tfsc = GetTfsClient(); 

      var stopWatch = new Stopwatch(); 
      stopWatch.Start(); 
      foreach (string s in PATHS) 
      { 
       allIds.AddRange(GetIdsFromHistory(s, tfsc)); 
      } 
      stopWatch.Stop(); 

      return stopWatch.Elapsed; 
     } 

     static List<int> GetIdsFromHistory(string path, VersionControlServer tfsClient) 
     { 
      if (tfsClient == null) 
      { 
       tfsClient = GetTfsClient(); 
      } 

      IEnumerable submissions = tfsClient.QueryHistory(
       path, 
       VersionSpec.Latest, 
       0, 
       RecursionType.None, // Assume that the path is to a file, not a directory 
       null, 
       null, 
       null, 
       Int32.MaxValue, 
       false, 
       false); 

      List<int> ids = new List<int>(); 
      foreach(Changeset cs in submissions) 
      { 
       ids.Add(cs.ChangesetId); 
      } 
      return ids; 
     } 
+0

作爲一個方面說明,我還[ (https://github.com/mrcaron/Tfs2010QueryHistoryPerfExp/blob/master/QueryHistoryPerformanceTesting/Program.cs)實現了上述功能,但它將結果放在WAY關閉的位置(不知道爲什麼):'Parallel Pre- Alloc:執行時間平均(ms):5418.07828;並行Alloc OnDemand:執行時間平均(毫秒):1312.86686;並行SameClient:平均執行時間(毫秒):370.89382;串行:執行時間平均(毫秒):288.3632' –

相關問題