2011-03-11 63 views
3

我試圖讓我的簡單C#圖形庫多線程。然而,引入該代碼後:多線程代碼中的頻繁滯後尖峯

/* foreach (IAffector affector in affectorLookup.Values) 
    affector.Update(timestep); */ 

taskManager.Value = timestep; taskManager.Start(); 

foreach (IAffector affector in affectorLookup.Values) 
    taskManager.AddToQueue(affector.Update); 
taskManager.StopWhenDone(); 
taskManager.Wait(); 

仿真開始遇到尖銳的滯後峯值,這似乎在TaskHandler.Run發起(我不能告訴是肯定的,因爲添加前面的代碼,使我的代碼探查器忽略TaskHandler.Run之外的任何內容)。

任務管理器:

public class TaskManager 
{ 
    public delegate void MethodDel(float timestep); 
    private Queue<MethodDel> queue; 
    private List<TaskHandler> handlers; 
    private float value; 


    public float Value 
    { 
     get 
     { 
      return value; 
     } 
     set 
     { 
      this.value = value; 
     } 
    } 


    public TaskManager() 
    { 
     this.queue = new Queue<MethodDel>(); 
     this.handlers = new List<TaskHandler>(System.Environment.ProcessorCount); 

     for (int t = 0; t < this.handlers.Capacity; ++t) 
      this.handlers.Add(new TaskHandler(this)); 

     this.value = 0; 
    } 


    public void Start() 
    { 
     foreach (var handler in handlers) 
      handler.Start(); 
    } 


    public void Stop() 
    { 
     lock (queue) 
      queue.Clear(); 

     foreach (var handler in handlers) 
      handler.StopWhenDone(); 
    } 


    public void StopWhenDone() 
    { 
     foreach (var handler in handlers) 
      handler.StopWhenDone(); 
    } 


    public void AddToQueue(MethodDel method) 
    { 
     lock (queue) 
      queue.Enqueue(method); 
    } 


    public bool GetFromQueue(out MethodDel method) 
    { 
     lock (queue) 
     { 
      if (queue.Count == 0) { method = null; return false; } 

      method = queue.Dequeue(); 
      return true; 
     } 
    } 


    public int GetQueueCount() 
    { 
     return queue.Count; 
    } 

    internal void Wait() 
    { 
     // Have to wait for them one at a time because the main thread is STA. 

     WaitHandle[] waitHandles = new WaitHandle[1]; 
     // for (int t = 0; t < handlers.Count; ++t) 
      // waitHandles[t] = handlers[t].WaitHandle; 

     // WaitHandle.WaitAll(waitHandles); 
     for (int t = 0; t < handlers.Count; ++t) 
     { waitHandles[0] = handlers[t].WaitHandle; WaitHandle.WaitAll(waitHandles); } 
    } 
} 

,任務處理:

public class TaskHandler 
{ 
    private TaskManager manager; 
    private Thread thread; 
    private bool stopWhenDone; 
    private ManualResetEvent waitHandle; 


    public ManualResetEvent WaitHandle 
    { 
     get 
     { 
      return waitHandle; 
     } 
    } 


    public TaskHandler(TaskManager manager) 
    { 
     this.manager = manager; 
    } 


    public void Start() 
    { 
     waitHandle = new ManualResetEvent(false); 

     stopWhenDone = false; 

     thread = new Thread(Run); 
     thread.IsBackground = true; 
     thread.SetApartmentState(ApartmentState.MTA); 
     thread.Start(); 
    } 


    public void StopWhenDone() 
    { 
     this.stopWhenDone = true; 
    } 

    // Possible source of slowdown 
    private void Run() 
    { 
     TaskManager.MethodDel curMethod; 
     while (!stopWhenDone || manager.GetQueueCount() > 0) 
     { 
      if (manager.GetFromQueue(out curMethod)) 
      { 
       curMethod(manager.Value); 
      } 
     } 
     waitHandle.Set(); 
    } 
} 

回答

3

啓動一個線程是一項繁重的操作。不知道它是否像你正在經歷的那樣沉重,但可能就是這樣。此外,讓所有的處理並行運行可能會給你的系統帶來很大的壓力,可能收益甚微......

+0

我有一個雙核心,所以我不認爲期待一些好處是不合理的。我會測試線程的開始時間,謝謝。 –

+0

現在很有趣。在調試或發行版中,它可以每秒創建100,000個新的Thread對象。但是,它可以在調試中每秒僅啓動100次,並且在發佈時每秒啓動2700次。 –

+0

線程花費類似於開始的400k週期,但創建一個新線程只是分配一個新的堆棧。就個人而言,設計看起來只能創建與內核一樣多的線程,然後每個線程只是在隊列中咀嚼。 – Bengie

0

我打算說,spike與waitHandle.Set()有關。

我喜歡整體設計,但我之前沒有使用過WaitHandle,所以我不確定這是如何與您的設計進行交互的。