微企免費網(wǎng)站建設(shè)制作網(wǎng)站要花多少錢
文章目錄
- 為什么需要創(chuàng)建并發(fā)限制?
- 不用并發(fā)限制會怎樣?
- 怎么設(shè)置并發(fā)限制
- 如果任務(wù)是I/O密集型,可以適當(dāng)放寬并發(fā)數(shù)嗎?
- 線程是有限資源,那4核8線程的CPU,線程數(shù)就是8嗎?
- CPU的“線程”與操作系統(tǒng)的“線程”
- 總結(jié)
protected async Task OperateModbusObjectToEnabledClientsAsync(Func<ModbusObjectDemo, Task> func, string operationName = ""){try{if (func == null)throw new ArgumentNullException(nameof(func));// 獲取所有已啟用寫操作的客戶端var enabledClients = _clientProvider.GetEnabledClients();if (!enabledClients.Any()){StatusMessage = "No enabled clients found!";logger.Debug(StatusMessage);return;}logger.Trace("Action:客戶端數(shù)量:{0},寫操作開始:{1}", enabledClients.Count(), operationName);// 使用一種輕量級的信號量來創(chuàng)建并發(fā)限制,用于限制同時運行的任務(wù)數(shù)量,避免同時處理太多客戶端。using var semaphore = new SemaphoreSlim(Environment.ProcessorCount);var tasks = new List<Task<ClientOperationResult>>();foreach (var client in enabledClients){tasks.Add(ProcessClientAsync(client, func, semaphore));}// 等待所有任務(wù)完成,設(shè)置超時時間var timeoutTask = Task.Delay(TimeSpan.FromSeconds(20)); // 設(shè)置20秒超時// 等待所有任務(wù)和超時任務(wù)完成。// 使用Action版本(無返回值),這里的WhenAll只會等待Task.Run完成,而不會等待action中的異步操作。// 使用Func<Task>的版本,這里的WhenAll會等待所有異步操作完成。var completedTask = await Task.WhenAny(Task.WhenAll(tasks), timeoutTask);if (completedTask == timeoutTask)//如果是超時任務(wù)完成則報錯{logger.Error($"Operation {operationName} timed out after 20 seconds");StatusMessage = $"Operation {operationName} timed out";return;}var results = await Task.WhenAll(tasks);logger.Trace("Action:客戶端數(shù)量:{0},寫操作結(jié)束:{1}", enabledClients.Count(), operationName);ProcessResults(results, operationName);}catch (Exception ex){StatusMessage = $"Error in {operationName}: {ex.Message}";logger.Debug(StatusMessage);}}private async Task<ClientOperationResult> ProcessClientAsync(IModbusTcpClient client, Func<ModbusObjectDemo, Task> func, SemaphoreSlim semaphore){try{// 等待獲取信號量await semaphore.WaitAsync();logger.Trace("信號量數(shù)量:{0}", semaphore.CurrentCount);if (!client.IsConnected){return new ClientOperationResult{Client = client,Success = false,Error = "Not connected"};}// 直接等待異步操作,不需要 Task.Runawait client.TryGetModbusObjectDemoAsync(func);return new ClientOperationResult{Client = client,Success = true,Error = string.Empty};}catch (Exception ex){logger.Error($"Error processing client {client.Name}: {ex.Message}");return new ClientOperationResult{Client = client,Success = false,Error = ex.Message};}finally{semaphore.Release();}}
為什么需要創(chuàng)建并發(fā)限制?
-
原因一:線程資源是有限的
- 線程不是“無限”資源。每創(chuàng)建一個線程,都會占用一定的內(nèi)存和CPU調(diào)度資源。
- 如果有很多客戶端(比如幾十、幾百個),每個都開一個線程去處理,系統(tǒng)很快就會因為線程數(shù)過多而變慢甚至崩潰(線程上下文切換開銷大,內(nèi)存消耗大)。
-
原因二:防止“線程風(fēng)暴”
- 如果不加限制,所有任務(wù)會同時并發(fā)執(zhí)行,可能導(dǎo)致CPU、內(nèi)存、網(wǎng)絡(luò)等資源被瞬間耗盡,影響整個系統(tǒng)的穩(wěn)定性。
-
原因三:提升整體吞吐量和響應(yīng)性
- 合理的并發(fā)數(shù)可以讓CPU和I/O資源得到充分利用,同時又不會讓系統(tǒng)過載。
- 這樣可以保證每個任務(wù)都能在合理的時間內(nèi)完成,系統(tǒng)整體更平穩(wěn)。
不用并發(fā)限制會怎樣?
- 可能會出現(xiàn)線程爆炸,導(dǎo)致系統(tǒng)變慢甚至崩潰。
- 任務(wù)之間會頻繁切換,CPU大部分時間都在做“線程切換”而不是“真正的工作”。
- 系統(tǒng)響應(yīng)變慢,甚至出現(xiàn)死鎖、內(nèi)存溢出等問題。
怎么設(shè)置并發(fā)限制
- 如示例代碼中使用輕量級的信號量(SemaphoreSlim),用于限制同時運行的任務(wù)數(shù)量。可以把它理解為“并發(fā)閘門”,只有拿到“通行證”的任務(wù)才能執(zhí)行,其他任務(wù)要等前面的任務(wù)釋放“通行證”后才能繼續(xù)。
Environment.ProcessorCount 的含義
- Environment.ProcessorCount 返回當(dāng)前機器的邏輯處理器(CPU核心)數(shù)量。
- 例如,4核8線程的CPU,這個值就是8。也就是說通過信號量(SemaphoreSlim)限制同時只允許8個線程同時處理。
為什么用它
- 這是一個經(jīng)驗值,通常來說,CPU密集型任務(wù)的并發(fā)數(shù)設(shè)置為核心數(shù)可以獲得最優(yōu)性能。
- 即使是I/O密集型任務(wù),設(shè)置為核心數(shù)也能保證不會有太多任務(wù)同時搶占CPU,避免線程切換開銷過大。
- 這樣做的好處是:既能利用多核CPU的并發(fā)能力,又不會讓系統(tǒng)資源被過度消耗。
- 舉例說明
- 假設(shè)你有100個客戶端,如果不加限制,100個任務(wù)會同時執(zhí)行,系統(tǒng)壓力很大。
- 如果用 SemaphoreSlim(8),每次只允許8個任務(wù)并發(fā)執(zhí)行,其他的排隊,等前面的執(zhí)行完再繼續(xù)。
如果任務(wù)是I/O密集型,可以適當(dāng)放寬并發(fā)數(shù)嗎?
- 可以!對于I/O密集型任務(wù)(比如網(wǎng)絡(luò)、磁盤操作),可以適當(dāng)把并發(fā)數(shù)調(diào)大一些(比如2~4倍核心數(shù)),但也要根據(jù)實際測試和服務(wù)器能力來調(diào)整。
- 但絕不能無限制地放大,否則還是會有資源耗盡的風(fēng)險。
線程是有限資源,那4核8線程的CPU,線程數(shù)就是8嗎?
- 我們可以創(chuàng)建遠遠多于8個線程(比如100、1000甚至更多),但這樣做會帶來:
- 內(nèi)存消耗(每個線程有自己的棧空間,通常1~2MB)
- 線程切換的CPU開銷(上下文切換)
- 系統(tǒng)調(diào)度壓力
- 實際“有限資源”取決于:
- 你的機器內(nèi)存有多少
- 你的操作系統(tǒng)允許的最大線程數(shù)
- 你的應(yīng)用場景(CPU密集型還是I/O密集型)
- 一般來說:
- 線程數(shù)在幾十到幾百是沒問題的(取決于內(nèi)存和場景)
- 但同一時刻能被CPU真正執(zhí)行的線程數(shù),就是邏輯核心數(shù)(比如8)
- 這就要引入下一個話題:CPU的“線程”與操作系統(tǒng)的“線程”
CPU的“線程”與操作系統(tǒng)的“線程”
-
CPU的線程(比如4核8線程)指的是CPU能同時并行執(zhí)行的最大任務(wù)數(shù),也就是“并發(fā)執(zhí)行單元”。
4核8線程,代表有4個物理核心,每個核心支持超線程(Hyper-Threading),所以一共8個“邏輯核心”。
這意味著最多有8個任務(wù)可以真正同時被CPU執(zhí)行。
-
操作系統(tǒng)的線程(比如C#里的Thread/Task)是軟件層面的調(diào)度單元。
操作系統(tǒng)可以創(chuàng)建成百上千個線程,但同一時刻只有最多8個線程能被CPU真正執(zhí)行(在4核8線程的機器上)。
其他線程會被掛起、等待,操作系統(tǒng)會不斷切換它們上CPU。
總結(jié)
- 4核8線程的CPU,不是說你只能創(chuàng)建8個線程,而是同一時刻最多8個線程能被CPU并行執(zhí)行。
- 你可以創(chuàng)建更多線程,但會帶來資源消耗和調(diào)度開銷。
- 線程是有限資源,但這個“有限”不是8,而是受內(nèi)存、操作系統(tǒng)和應(yīng)用場景共同決定的。
- 用 Environment.ProcessorCount 作為并發(fā)限制,是為了讓你的程序既高效又不會讓系統(tǒng)過載(頻繁在線程上下文中切換)。