我注意到在我的流程中發生了初始減速,並且在進行多次hangdumps時,我能夠使用以下代碼隔離問題並重現該情形。我正在使用一個具有鎖定功能的庫,它最終會調用某些方法的用戶端實現。這些方法使用httpclient進行異步調用。這些異步調用是從庫內的這些鎖中進行的。使用具有鎖定的異步調用速度減慢
現在,我正在發生什麼的理論(糾正我,如果我錯了): 獲取旋轉的任務嘗試獲取鎖並保持足夠快的線程,以便第一個PingAsync方法需要等待默認的任務調度程序啓動一個新的線程以便運行,這是基於默認的.net調度算法的0.5秒。這就是爲什麼我認爲我注意到任務總數大於32的延遲,這也隨着總任務數的增加而線性增加。
解決方法:
- 增加minthreads算,我認爲這是治標,而不是實際的問題。
- 另一種方法是有一個有限的併發來控制任務的數量。但這些都是由傳入HTTPRequests的網絡服務器紡任務,通常我們不會擁有控制權(還是我們?)
我明白,結合ASYC和非異步是糟糕的設計,並使用sempahores'異步調用將是更好的方式去。假設我無法控制這個圖書館,那麼如何減輕這個問題呢?
const int ParallelCount = 16;
const int TotalTasks = 33;
static object _lockObj = new object();
static HttpClient _httpClient = new HttpClient();
static int count = 0;
static void Main(string[] args)
{
ThreadPool.GetMinThreads(out int workerThreads, out int ioThreads);
Console.WriteLine($"Min threads count. Worker: {workerThreads}. IoThreads: {ioThreads}");
ThreadPool.GetMaxThreads(out workerThreads, out ioThreads);
Console.WriteLine($"Max threads count. Worker: {workerThreads}. IoThreads: {ioThreads}");
//var done = ThreadPool.SetMaxThreads(1024, 1000);
//ThreadPool.GetMaxThreads(out workerThreads, out ioThreads);
//Console.WriteLine($"Set Max Threads success? {done}.");
//Console.WriteLine($"Max threads count. Worker: {workerThreads}. IoThreads: {ioThreads}");
//var done = ThreadPool.SetMinThreads(1024, 1000);
//ThreadPool.GetMinThreads(out workerThreads, out ioThreads);
//Console.WriteLine($"Set Min Threads success? {done}.");
//Console.WriteLine($"Min threads count. Worker: {workerThreads}. IoThreads: {ioThreads}");
var startTime = DateTime.UtcNow;
var tasks = new List<Task>();
for (int i = 0; i < TotalTasks; i++)
{
tasks.Add(Task.Run(() => LibraryMethod()));
//while (tasks.Count > ParallelCount)
//{
// var task = Task.WhenAny(tasks.ToArray()).GetAwaiter().GetResult();
// if (task.IsFaulted)
// {
// throw task.Exception;
// }
// tasks.Remove(task);
//}
}
Task.WaitAll(tasks.ToArray());
//while (tasks.Count > 0)
//{
// var task = Task.WhenAny(tasks.ToArray()).GetAwaiter().GetResult();
// if (task.IsFaulted)
// {
// throw task.Exception;
// }
// tasks.Remove(task);
// Console.Write(".");
//}
Console.Write($"\nDone in {(DateTime.UtcNow-startTime).TotalMilliseconds}");
Console.ReadLine();
}
假設這是其中文庫的方法被稱爲部分,
public static void LibraryMethod()
{
lock (_lockObj)
{
SimpleNonAsync();
}
}
最終,該方法的用戶執行被調用其是異步。
public static void SimpleNonAsync()
{
//PingAsync().Result;
//PingAsync().ConfigureAwaiter(false).Wait();
PingAsync().Wait();
}
private static async Task PingAsync()
{
Console.Write($"{Interlocked.Increment(ref count)}.");
await _httpClient.SendAsync(new HttpRequestMessage
{
RequestUri = new Uri([email protected]"http://127.0.0.1"),
Method = HttpMethod.Get
});
}
異步調用上的阻塞破壞了異步的整個目的。 – SLaks
是的。我明白那個。考慮到我沒有修改這個庫的權限,你是否建議使用非異步httpclient調用(假設有一個),這會影響性能,因爲它是一個io操作? – iambatman
請注意[任務(仍然)不是線程](https://blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-are-still-not-threads-and-async-is-not -parallel /)... –