[英]Performing concurrent tasks in C#
我有一項服務,需要盡快讀取來自Amazon SQS的消息。 我們期望流量很大,我希望能夠每秒讀取1萬條以上的消息。 不幸的是,我目前大約每秒10條消息。 顯然,我有工作要做。
這就是我正在使用的(轉換為控制台應用程序以簡化測試):
private static int _concurrentRequests;
private static int _maxConcurrentRequests;
public static void Main(string[] args) {
_concurrentRequests = 0;
_maxConcurrentRequests = 100;
var timer = new Timer();
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.Interval = 10;
timer.Enabled = true;
Console.ReadLine();
timer.Dispose();
}
public static void OnTimedEvent(object s, ElapsedEventArgs e) {
if (_concurrentRequests < _maxConcurrentRequests) {
_concurrentRequests++;
ProcessMessages();
}
}
public static async Task ProcessMessages() {
var manager = new MessageManager();
manager.ProcessMessages(); // this is an async method that reads in the messages from SQS
_concurrentRequests--;
}
我沒有收到接近100個並發請求,而且似乎也不是每10毫秒觸發一次OnTimedEvent
。
我不確定Timer
是否是正確的方法。 我對這種編碼沒有太多的經驗。 我樂於嘗試任何事情。
更新
多虧了calebboyd,我才更接近實現自己的目標。 這是一些非常糟糕的代碼:
private static SemaphoreSlim _locker;
public static void Main(string[] args) {
_manager = new MessageManager();
RunBatchProcessingForeverAsync();
}
private static async Task RunBatchProcessingForeverAsync() {
_locker = new SemaphoreSlim(10, 10);
while (true) {
Thread thread = new Thread(new ParameterizedThreadStart(Process));
thread.Start();
}
}
private static async void Process(object args) {
_locker.WaitAsync();
try {
await _manager.ProcessMessages();
}
finally {
_locker.Release();
}
}
這樣我可以接近每秒讀取大量消息,但是問題是我的ProcessMessages
調用永遠不會完成(或者可能會在很長一段時間后完成)。 我想我可能需要限制我一次運行的線程數。
關於如何改進此代碼,以便使ProcessMessages
有機會完成的任何建議?
因為未等待MessageManager對象上的ProcessMessages
方法,所以我將假定它綁定到在其中執行的同一線程。僅將函數標記為async
不會將工作傳遞給新線程。 在這種假設下,該代碼實際上並未在多個線程中執行。 您可以使用以下代碼在更多線程池中執行代碼。
管理器對象可能無法處理並發使用。 因此,我在Task.Run lambda中創建了它。 這也可能是昂貴的,因此是不切實際的。
async Task RunBatchProcessingForeverAsync () {
var lock = new SemaphoreSlim(initialCount: 10);
while (true) {
await lock.WaitAsync();
Task.Run(() => {
try {
var manager = new MessageManager();
manager.ProcessMessages();
} finally {
lock.Release();
}
});
}
}
我已經有一段時間沒有寫C#了,但這應該可以同時,重復,永久地運行您的方法10次。
如@calebboyd所建議,首先必須使線程異步。 現在,如果您去這里- 調用API時在何處使用並發性 ,您將看到一個異步線程足以用於快速地池化網絡資源。 如果您能夠在一個請求中從亞馬遜獲取多條消息,那么您的生產者線程(對亞馬遜進行異步調用的那個)就可以了-每秒可以發送數百個請求。 這不會成為您的瓶頸。 但是,處理接收到的數據的繼續任務將被移交給線程池。 在這里,您有機會遇到瓶頸-假設每秒有100條響應到達,每個響應包含100條消息(達到您的10K msgs / sec近似值)。 每秒鍾您有100個新任務,每個任務都需要您的線程來處理100條消息。 現在有兩個選項:(1)這些消息的處理不受CPU限制-您只需將它們發送到DB,或者(2)執行消耗CPU的計算,例如科學計算,序列化或某些繁重的業務邏輯。 如果您的情況是(1),則將瓶頸向后推向DB。 如果為(2),則您別無選擇,只能放大/縮小或優化計算。 但是您的瓶頸可能不是生產線程-如果實現正確(請參閱上面的鏈接以獲取示例)。
我假設異步方法在線程池中排隊,該線程池的線程數與可用處理器的數量相同。 您可能會生成100個請求,但它們仍由8個線程執行。 嘗試創建N個線程的數組並將其使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.