2012-04-18 18 views
2

我需要在2毫秒內執行strategy.AllTablesUpdated(); 50個策略(我需要重複每秒〜500次)。 使用下面的代碼我發現,只是Monitor.TryEnter呼叫拼寫高達1毫秒(!!!),我做了50次!如何有效地執行每秒500次並行方法500次?

// must be called ~500 times per second 
    public void FinishUpdatingTables() 
    { 
     foreach (Strategy strategy in strategies) // about ~50, should be executed in 2 ms 
     { 
      // this slow and can be paralleled 
      strategy.AllTablesUpdated(); 
     } 
    } 

...................

public override bool AllTablesUpdated(Stopwatch sw) 
    { 
     this.sw = sw; 
     Checkpoint(this + " TryEnter attempt "); 
     if (Monitor.TryEnter(desiredOrdersBuy)) 
     { 
      Checkpoint(this + " TryEnter success "); 
      try 
      { 
       OnAllTablesUpdated(); 
      } finally 
      { 
       Monitor.Exit(desiredOrdersBuy); 
      } 
      return true; 
     } else 
     { 
      Checkpoint(this + " TryEnter failed "); 
     } 
     return false; 
    } 

    public void Checkpoint(string message) 
    { 
     if (sw == null) 
     { 
      return; 
     } 
     long time = sw.ElapsedTicks/(Stopwatch.Frequency/(1000L * 1000L)); 
     Log.Push(LogItemType.Debug, message + time); 
    } 

從日誌(以微秒),失敗的嘗試用時1毫秒〜:

12:55:43:778調試:TryEnter嘗試1264 12:55:43:779調試:TryEnter失敗2123

從日誌(以μs爲單位),成功嘗試用時〜0.01ms:

12:55:49:701調試:TryEnter嘗試889 12:55:49:701調試:TryEnter成功900

所以現在我認爲Monitor.TryEnter是太貴了,我一個50個戰略執行一個。所以我想利用平行像Task這項工作:

// must be called ~500 times per second 
    public void FinishUpdatingTables() 
    { 
     foreach (Strategy strategy in strategies) // about ~50, should be executed in 2 ms 
     { 
      // this slow and can be paralleled 
      Task.Factory.StartNew(() => { 
       strategy.AllTablesUpdated(); 
      }); 
     } 
    } 

我也可能會取代Monitor.TryEnter只是lock與這樣的做法一切都將是異步的。

我的問題:

  • 爲什麼Monitor.TryEnter是如此之慢? (如果沒有獲得鎖,則爲1毫秒)
  • 啓動50 Task每秒2毫秒= 25,000任務每秒? .NET可以有效地管理這個嗎?我也可以使用BlockingCollection生產者 - 消費者模式,並且只開始50個「工作人員」,然後每2毫秒向BlockingCollection提交50個物品的新包裝?這會更好嗎?
  • 你將如何執行50個方法,每個方法可以並行2 ms(每秒500次),每秒總共25 000次?
+0

如果您使用任務,它將花費超過2ms,因爲任務不會立即開始。 – 2012-04-18 09:52:08

回答

4
  1. Monitor.TryEnter(對象)是剛剛Monitor.TryEnter(對象,0,參考假)(0毫秒超時)。如果沒有獲得鎖定,那麼1毫秒只是嘗試獲取鎖定的開銷。
  2. 你可以根據需要開始任意多的任務,它們都使用ThreadPool,但它將被限制爲最大線程數。最大值取決於你的系統,核心數量,內存等等。但它肯定不會是25,000個線程。但是,如果您開始使用TPL調度程序,您將遇到麻煩。我只是使用Parallel.Foreach,看看它有多遠。
  3. Parallel.ForEach。我還要確保strategies的類型爲IList,因爲許多項目都是在不等待迭代器的情況下觸發的。

您還沒有將代碼粘貼到OnAllTablesUpdated(),您在該過程的持續時間內保持鎖定狀態。這將成爲你們所有人的瓶頸。

有些問題,爲什麼當表準備好處理時使用鎖?

  1. 代表不可能嗎?
  2. 爲什麼在運行策略時將其鎖定?你是否在修改每個策略中的表格?如果情況確實如此,你能不能拿到它的副本?
+0

1.對不起,我不明白問題。 2.「OnAllTablesUpdated」重新計算'desiredOrdersBuy'。另外我有'OrdersExecutor',它使用'desiredOrderBuy'來下訂單。所以有兩個不兼容的任務 - 重新計算訂單和下訂單。雖然訂單正在重新計算執行者不應該試圖下達訂單,這就是爲什麼每個策略都會鎖定期望訂單的時候重新計算並且每個執行者都會在發出訂單時鎖定相同的訂單 – javapowered 2012-04-18 10:45:14

+0

@javapowered忽略問題1,因爲您的答案爲2。 OrderExecutor將執行訂單,而不管策略是否更新?即它鎖定了desiredOrders,執行它,這就是爲什麼對於你的一些情況,Strategy.AllTablesUpdated無法獲得鎖?訂單是否如此敏感以至於您更喜歡執行舊策略?或者你是否可以不通知你的OrderExecutor策略已經更新,完成後請執行命令(並失去鎖定) – 2012-04-18 11:08:10

+0

@M Afifi非常感謝你試圖深入理解我的程序的邏輯:)如果'OrdersExecutor'鎖定' desiredOrdersBuy「,那麼這意味着現在訂單和」執行「(推送,放置)的過程!所以是的,有時(儘管不是)Strategy.AllTablesUpdated無法獲得鎖,那麼它會在下一次迭代中完成。如果訂單已經「執行」我不能取消。我只能等到完成後更新desiredOrdersBuy,然後'OrdersExecutor'會根據請求更改訂單。你看到什麼問題,你想達到什麼目的? – javapowered 2012-04-18 11:17:51