我有兩個應用程序使用 NetMQ 相互通信。第一個應用程序內置了一個 API 控制器,第二個應用程序(引擎)在必要時與第一個應用程序來回通信。所有 API 控制器函數都是異步任務,因為我們期望同時發生許多請求。其中一個 API 控制器函數需要首先向第二個應用程序(引擎)發送 NetMQ 消息,并等待其響應,然后再將結果返回給 API 函數請求者(API 用戶)。它需要以異步方式完成所有這些,而不需要從線程池中獲取,因為正如我之前所說,我們可能有大量請求不斷地訪問此控制器。當 SemaphoreSlim 等待時,第二個應用程序(引擎)正在更新數據庫中特定記錄的狀態,并且我需要在 API 控制器函數中讀取該記錄的狀態并將其返回給 API 用戶,如果我沒有 SemaphoreSlim 等待設計,那么代碼當然會提前讀取數據庫記錄并且不會返回正確的狀態。因此,解決方案是創建等待,允許引擎更新狀態,然后通知 API 控制器函數它可以從數據庫讀取新更新的狀態。一位同事告訴我,使用 SemaphoreSlim 和 Dictionary 設置通過鎖定在函數中創建等待,并且僅在收到來自引擎的 NetMQ 回復時才從字典中釋放 SemaphoreSlim。然后代碼自然會繼續,我將檢查數據庫的更新狀態并將其返回給 API 用戶。問題在于 API 控制器函數完全忽略了 SemaphoreSlim 鎖并過早地從數據庫讀取。最有可能的是因為它是異步的,所以我的問題是,如何使 SempahoreSlim 鎖定和讀取數據庫代碼以異步方式一起工作,而不鎖定線程?我相信代碼會讓您更好地理解我正在嘗試做的事情。1. API控制器部分功能代碼://Call EngineMessenging.Queue.Enqueue(OrderPositionText + " " + UserId + " " + AssetPairId + " " + OrderType + " " + OrderRequestId + " " + rAmount + " " + rPrice + " " + Stop);//Create SemaphoreSlim Lock and add it to global Dictionary var NewSemaphore = new SemaphoreSlim(1, 1);await NewSemaphore.WaitAsync().ConfigureAwait(false);ApiHub.UserSemaphoreDictionary.TryAdd(UserId, NewSemaphore);//Lock was released lets now read record in Database then return appropriate result to API user. Debug.WriteLine("Checking order status for " + uuid); OrderRequest OrderRequestRec = await _context.OrderRequest.Where(x => x.UUID == uuid).FirstOrDefaultAsync(); if (OrderRequestRec != null) { Debug.WriteLine("ORDER STATUS: " + OrderRequestRec.Status); }2.這是另一個類中接收NetMQ消息并釋放SemaphoreSlim的代碼: //API Semaphore Release lock SemaphoreSlim checkUserId; if (ApiHub.UserSemaphoreDictionary.TryGetValue(UserId, out checkUserId)) { Debug.WriteLine("RELEASING LOCK: " + UserId); checkUserId.Release(); }結果是先讀取Database,然后釋放SemaphoreSlim。我需要釋放 SemaphoreSlim,然后讀取數據庫。
1 回答

慕婉清6462132
TA貢獻1804條經驗 獲得超2個贊
對于這個場景,我使用 aTaskCompletionSource<bool>
作為一種高級事件。它具有Task
可直接等待的屬性。然后,您可以從接收消息的類中調用SetResult(true)
來完成任務并允許等待者繼續。
請注意,類型bool
是無關緊要的:任務必須具有某種結果類型,但在這里我們只是將其用作表示事件已發生的信號方式(也就是說,我們實際上只想要 a 等待,而不是Task
a Task<T>
) 。
編輯以響應OP的編輯:
是的,這是完全異步的。該await
關鍵字使 C# 編譯器將控制器的代碼分解為多個部分,因此后面的部分成為await
可以稍后恢復的“延續”。它將延續添加到任務的完成處理程序列表中,然后放棄線程;控制器方法不再執行,因此不會耗盡線程/堆棧。
在您調用的類中SetResult
,這實際上會導致任務通知(執行)處理程序,從而導致控制器的代碼喚醒并完成。無需創建額外的線程;現有的被使用,并且一旦沒有立即要做的工作就被放棄。
- 1 回答
- 0 關注
- 106 瀏覽
添加回答
舉報
0/150
提交
取消