[英]Implicit vs. Explicit .NET Threading
因此,當我在任務管理器中查看套接字服務器解決方案時,我注意到該應用程序正在創建和銷毀線程(從9到10到15再到10)。 其中一些可以由MySQL連接器使用,但是登錄到服務器似乎會創建一個新線程。
我正在使用異步套接字來接受連接,將其傳遞給PacketHandler實例,同時監聽並發連接。 我的問題是,我怎么知道什么代碼將創建一個新線程? 我從未明確地寫過需要創建一個線程,但這似乎是使用異步套接字的結果。 我知道創建線程(Cores * 2 =線程的目標數量)時應該保守一些,但是當您不知道什么代碼會自然地創建新線程時,這是一項艱巨的任務。
正確的異步代碼僅將線程用於回調,而回調通常只需要線程池線程,並且除非您做錯了事,否則無需增加新的線程池線程。
對於MS.NET中當前的Socket實現,確實是這種情況。 它們在異步事件上注冊回調,並且在該回調期間使用線程池線程。 除非您在回調中執行同步I / O操作,否則在線程池上創建更多線程是沒有意義的。
不要看任務管理器,而是看調試器。 您看到的線程可能與您正在使用的庫無關。 .NET中有很多基礎結構線程可以創建和處理-特別是垃圾收集器。 檢查這些線程上的堆棧跟蹤,您將知道它們的作用。
編輯 :在這種情況下,似乎MySQL連接器確實不使用異步I / O。 它只是假裝使用多線程將同步I / O異步化-換句話說,異步API完全是無用的bollocks :)並且由於它們使用線程池線程進行同步I / O,因此您將獲得大量問題。 您可以使用其他庫,也可以避免使用異步API-它在騙您。
不幸的是,MySQL Connector沒有提供真正的異步方法。 它的Begin/End
方法通過將同步版本包裝在線程中來偽造異步執行:
public IAsyncResult BeginExecuteReader(CommandBehavior behavior)
{
if (caller != null)
Throw(new MySqlException(Resources.UnableToStartSecondAsyncOp));
caller = new AsyncDelegate(AsyncExecuteWrapper);
asyncResult = caller.BeginInvoke(1, behavior, null, null);
return asyncResult;
}
internal object AsyncExecuteWrapper(int type, CommandBehavior behavior)
{
thrownException = null;
try
{
if (type == 1)
return ExecuteReader(behavior);
return ExecuteNonQuery();
}
catch (Exception ex)
{
thrownException = ex;
}
return null;
}
結果,他們浪費了線程等待響應。 三年前提交了一個錯誤, 但從未得到真正的答案
這就是創建替代MySQLConnector項目的原因,該項目提供了真正的異步操作以及.NET Core支持,例如, 這種方法
internal async Task<int> ExecuteNonQueryAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
{
using (var reader = (MySqlDataReader) await ExecuteReaderAsync(CommandBehavior.Default, ioBehavior, cancellationToken).ConfigureAwait(false))
{
do
{
while (await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false))
{
}
} while (await reader.NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false));
return reader.RecordsAffected;
}
}
您會看到ReadAsync也是一種適當的異步方法
區別非常明顯,尤其是在Web應用程序中,因為它允許您使用較小的 VM實例來處理相同的流量。 或者同一台VM可以處理更多流量。 無論如何,價格差異是真實的。
發生這種情況是因為異步網絡操作實際上已轉移到驅動程序或主機上。 僅當網絡驅動程序將響應傳遞給應用程序時,才使用IO線程池中的線程。 在半虛擬化的情況下,卸載可以一直到主機。
另一方面,偽造的同步操作會阻塞,甚至可能導致繁忙的等待。 這是因為同步原語旨在用於... 同步對共享資源的訪問 。 掛起線程的成本很高,因此等待原語首先以自旋鎖開始,僅在經過一定時間后才掛起線程。
這就是為什么不考慮異步性的Web應用程序在等待遠程響應時最終會消耗大量CPU的原因
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.