我的应用程序通过http连接到大量客户端,从这些客户端下载数据并在收到这些结果时处理数据。每个请求都在一个单独的线程中发送,以便主线程不会被占用。
我们已经开始遇到性能问题,看起来这些问题主要与ThreadPool
中仅等待从这些请求中获取数据的大量线程有关。我知道在.NET 4.5中我们有async
和await
用于同一类型的问题,但我们仍在使用.NET 3.5。
对于在不同线程中发送这些请求的最佳方式有什么想法,但不是为了让该线程保持活动状态,而是在等待请求返回?
答案 0 :(得分:3)
您可以在.NET 3.5中使用异步操作,它不像.NET 4.5那样方便。大多数IO方法都有BeginX
/ EndX
方法对,它是X
方法的异步等效项。这称为Asynchronous Programming Model (APM)。
例如,您可以使用Stream.Read
和Stream.BeginRead
代替Stream.EndRead
。
实际上,.NET 4.5中的许多异步IO方法只是围绕Begin / End方法的包装。
答案 1 :(得分:1)
如果您无法使用.NET 4.x和async/await
,您仍然可以使用IEnumerator
和yield
实现某种类似的行为。它允许使用带有Begin/End
样式回调的伪同步线性代码流,包括using
,try/finally
,while/for/foreach
等语句。尽管你cannot use try/catch
异步枚举器驱动程序有一些实现,例如Jeffrey Richter's 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();
}
}
}