我的应用程序通过http连接到大量客户端,从这些客户端下载数据,并在收到这些结果时处理数据。每个请求都在一个单独的线程中发送,这样主线程就不会被占用。
我们已经开始遇到性能问题,似乎这些问题主要与ThreadPool
中的大量线程有关,这些线程正在等待从这些请求中获取数据。我知道在。net 4.5中,我们有async
和await
来解决相同类型的问题,但我们仍然使用。net 3.5。
你可以在。net 3.5中使用异步操作,只是不像。net 4.5那么方便。大多数IO方法都有BeginX
/EndX
方法对,这是X
方法的异步等效。这被称为异步编程模型(APM)。
例如,您可以使用Stream.BeginRead
和Stream.EndRead
而不是Stream.Read
。
实际上,. net 4.5中的许多异步IO方法只是围绕Begin/End方法的包装。
如果你不能使用。net 4。x和async/await
,您仍然可以使用IEnumerator
和yield
实现类似的行为。它允许使用伪同步线性代码流与Begin/End
风格的回调,包括语句像using
, try/finally
, while/for/foreach
等。但是不能使用try/catch
。
有一些异步枚举器驱动的实现,例如Jeffrey Richter的AsyncEnumerator
。
我过去用过类似下面的东西:
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();
}
}
}