我注意到在我的流程中发生了初始减速,并且在进行多次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 /)... –