C# 线程池中有大量线程处于等待状态,导致性能问题
我的应用程序通过http连接到大量客户端,从这些客户端下载数据,并在收到这些结果时处理数据。每个请求都在一个单独的线程中发送,这样主线程就不会被占用 我们开始遇到性能问题,这些问题似乎主要与线程池中的大量线程有关,这些线程正在等待从这些请求中获取数据。我知道在.NET4.5中,我们有异步问题,并等待相同类型的问题,但我们仍在使用.NET3.5C# 线程池中有大量线程处于等待状态,导致性能问题,c#,multithreading,.net-3.5,threadpool,scalability,C#,Multithreading,.net 3.5,Threadpool,Scalability,我的应用程序通过http连接到大量客户端,从这些客户端下载数据,并在收到这些结果时处理数据。每个请求都在一个单独的线程中发送,这样主线程就不会被占用 我们开始遇到性能问题,这些问题似乎主要与线程池中的大量线程有关,这些线程正在等待从这些请求中获取数据。我知道在.NET4.5中,我们有异步问题,并等待相同类型的问题,但我们仍在使用.NET3.5 您认为在不同的线程中发送这些请求的最佳方式是什么,而不是让该线程保持活动状态,而它所做的一切就是一直等待请求返回吗?您可以在.NET 3.5中使用异步操作
您认为在不同的线程中发送这些请求的最佳方式是什么,而不是让该线程保持活动状态,而它所做的一切就是一直等待请求返回吗?您可以在.NET 3.5中使用异步操作,但它没有.NET 4.5中方便。大多数IO方法都有一个BeginX/EndX方法对,它是X方法的异步等价物。这就是所谓的 例如,您可以使用Stream.BeginRead和Stream.EndRead代替Stream.Read
实际上,.NET 4.5中的许多异步IO方法只是围绕Begin/End方法的包装。您可以在.NET 3.5中使用异步操作,但它没有.NET 4.5中方便。大多数IO方法都有一个BeginX/EndX方法对,它是X方法的异步等价物。这就是所谓的 例如,您可以使用Stream.BeginRead和Stream.EndRead代替Stream.Read
实际上,.NET 4.5中的许多异步IO方法只是Begin/End方法的包装。如果不能使用.NET 4.x和异步/await,您仍然可以使用IEnumerator和yield实现类似的行为。它允许使用带有Begin/End样式回调的伪同步线性代码流,包括像using、try/finally、while/for/foreach等语句 异步枚举器驱动程序有几种实现,例如 我过去用过类似下面的东西:
class AsyncIO
{
void ReadFileAsync(string fileName)
{
AsyncOperationExt.Start(
start => ReadFileAsyncHelper(fileName, start),
result => Console.WriteLine("Result: " + result),
error => Console.WriteLine("Error: " + error));
}
static IEnumerator<object> ReadFileAsyncHelper(string fileName, Action nextStep)
{
using (var stream = new FileStream(
fileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1024, useAsync: true))
{
IAsyncResult asyncResult = null;
AsyncCallback asyncCallback = ar => { asyncResult = ar; nextStep(); };
var buff = new byte[1024];
while (true)
{
stream.BeginRead(buff, 0, buff.Length, asyncCallback, null);
yield return Type.Missing;
int readBytes = stream.EndRead(asyncResult);
if (readBytes == 0)
break;
// process the buff
}
}
yield return true;
}
}
// ...
// implement AsyncOperationExt.Start
public static class AsyncOperationExt
{
public static void Start<TResult>(
Func<Action, IEnumerator<TResult>> start,
Action<TResult> oncomplete,
Action<Exception> onerror)
{
IEnumerator<TResult> enumerator = null;
Action nextStep = () =>
{
try
{
var current = enumerator.Current;
if (!enumerator.MoveNext())
oncomplete(current);
}
catch (Exception ex)
{
onerror(ex);
}
enumerator.Dispose();
};
try
{
enumerator = start(nextStep);
}
catch (Exception ex)
{
onerror(ex);
enumerator.Dispose();
}
}
}
如果不能使用.NET4.x和async/await,那么仍然可以使用IEnumerator和yield实现类似的行为。它允许使用带有Begin/End样式回调的伪同步线性代码流,包括像using、try/finally、while/for/foreach等语句 异步枚举器驱动程序有几种实现,例如 我过去用过类似下面的东西:
class AsyncIO
{
void ReadFileAsync(string fileName)
{
AsyncOperationExt.Start(
start => ReadFileAsyncHelper(fileName, start),
result => Console.WriteLine("Result: " + result),
error => Console.WriteLine("Error: " + error));
}
static IEnumerator<object> ReadFileAsyncHelper(string fileName, Action nextStep)
{
using (var stream = new FileStream(
fileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1024, useAsync: true))
{
IAsyncResult asyncResult = null;
AsyncCallback asyncCallback = ar => { asyncResult = ar; nextStep(); };
var buff = new byte[1024];
while (true)
{
stream.BeginRead(buff, 0, buff.Length, asyncCallback, null);
yield return Type.Missing;
int readBytes = stream.EndRead(asyncResult);
if (readBytes == 0)
break;
// process the buff
}
}
yield return true;
}
}
// ...
// implement AsyncOperationExt.Start
public static class AsyncOperationExt
{
public static void Start<TResult>(
Func<Action, IEnumerator<TResult>> start,
Action<TResult> oncomplete,
Action<Exception> onerror)
{
IEnumerator<TResult> enumerator = null;
Action nextStep = () =>
{
try
{
var current = enumerator.Current;
if (!enumerator.MoveNext())
oncomplete(current);
}
catch (Exception ex)
{
onerror(ex);
}
enumerator.Dispose();
};
try
{
enumerator = start(nextStep);
}
catch (Exception ex)
{
onerror(ex);
enumerator.Dispose();
}
}
}
@保罗斯诺,是的;BeginRead方法是非阻塞的,当IO操作完成时,您传递给它的回调将在线程池线程上调用,而不会浪费线程等待。正是我想要的:为了补充答案,BeginX/EndX方法使用IO线程,这些线程在内部使用IO完成端口进行非阻塞IO调用。此外,建议不要显式创建线程,而是将任务排队到线程池中,让它为您管理线程。只有当现有线程正在运行或等待时,线程池才会创建线程。使用IOCP,可以最大限度地减少等待时间——因此,如果线程池发送m个请求,其中n个线程m>=n,也就不足为奇了;BeginRead方法是非阻塞的,当IO操作完成时,您传递给它的回调将在线程池线程上调用,而不会浪费线程等待。正是我想要的:为了补充答案,BeginX/EndX方法使用IO线程,这些线程在内部使用IO完成端口进行非阻塞IO调用。此外,建议不要显式创建线程,而是将任务排队到线程池中,让它为您管理线程。只有当现有线程正在运行或等待时,线程池才会创建线程。使用IOCP,您可以最大限度地减少等待时间——因此,如果线程池发送n个线程m>=n的m个请求,也就不足为奇了。