[英]Releasing threads during async tasks
我有一个系统,该系统产生许多必须并行运行的子流程
我们在线程计数方面遇到麻烦,因此我们想通过在等待远程API时尝试释放线程来减少活动线程的数量。
最初,我们使用WebRequest.GetResponse()
进行API调用,该调用自然是在等待API的同时持有空闲线程。
我们开始使用EAP模型(基于事件的异步编程...使用IAsyncResult的所有各种.NET方法),在此我们称为BeginGetResponse(CallbackTrigger)
,其中WaitHandle
被传递回主线程,然后触发后线程。 API处理。
据我们了解,这意味着子进程线程终止,并且回调是由网卡级中断触发的,该中断触发了新线程来发起回调。 也就是说,在等待API调用时,没有线程在等待运行CallbackTrigger
。
如果人们可以确认这种理解,那会很好吗?
现在,我们正在考虑使用await
可用的WebRequest.GetResponseAsync()
迁移到TPL模型(任务并行库... Task<T>
)。 我给人的印象是,这是await
\\ async
所做的工作的一部分……当远程源等待时, await
将控制权传递回调用堆栈,如果我启动了一堆可await
Tasks
然后调用Tasks
。 WaitAll
然后将不会为每个Task保持线程, 而该任务正在远程API上等待 。
我是否正确理解了这一点?
如果人们可以确认这种理解,那会很好吗?
是。 请注意, IAsyncResult
/ Begin*
/ End*
模式是APM,而不是EAP。 EAP是WebClient
的方法,其中DownloadAsync
方法在完成后会触发DownloadCompleted
事件。
APM / EAP是完成异步工作的硬方法,但实际上是异步的(意味着,它们不会占用线程只是为了阻止I / O完成)。 它们之所以“困难”,是因为它们使您的代码复杂得多,以至于大多数开发人员从未使用过它们,而只是停留在同步代码上。
我是否正确理解了这一点?
是。 通常,.NET中的所有异步I / O都使用单个I / O完成端口实现,该端口作为线程池的一部分存在。 无论API是APM,EAP还是TAP,都是如此。
使用TAP进行async
/ await
的整个想法是,核心Task
(如从GetResponseAsync
返回的GetResponseAsync
)仍建立在同一异步I / O系统上,然后async
/ await
使使用它们变得更加愉快。 您可以保持与await
相同的方法,而不用弄乱回调(APM)或事件处理程序(EAP)。
有趣的一点是, Task
实际上实现了IAsyncResult
,从高级角度看,APM和TAP非常相似( IAsyncResult
和Task
表示“正在运行”的操作)。
您应该发现TAP代码比当前的APM / EAP代码明显更简单(并且更易于维护!),并且性能没有明显变化。
( HttpClient
,考虑迁移到HttpClient
,它是从头开始设计的,而不是TAP固定在上面的HttpWebRequest
/ WebClient
)。
然而...
我有一个产生很多必须并行运行的子流程的系统...
通过这种“管道”,您可能要考虑转换为TPL Dataflow。 数据流既了解同步(异步)工作,又具有对节流的内置支持。 与TAP相比,Dataflow方法甚至可以进一步简化代码。
除了@Stephen Cleary的答案,我还进行了简短的测试来进一步证明这一点。
下面的代码在运行Synchronous方法时,无需对SetMinThreads进行修改,并且在定位需要花费几秒钟返回时间的网站时,将为每个请求保持一个打开的线程。 它将显示越来越多的活动线程,它会立即启动前几个任务,但在达到ThreadPool
的限制时会“阻塞”,并且仅每半秒或在旧请求结束时才启动新线程。
设置更高的MinThreadCount可以按预期推迟该问题。
保持MinThread Count不变,但是切换到Asynchronous(APM)方法或Await(TAP)方法将立即启动所有Task,并且在任何时候处于活动状态的线程数都保持较低。
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace LockTraceParser
{
internal class AsyncThreadsTester
{
public void Run()
{
//ThreadPool.SetMinThreads(100, 100);
Console.WriteLine("Beginning Test: ");
LogThreadCounts();
Test();
}
private void Test()
{
LogThreadCounts();
for (int i = 0; i < 65; i++)
{
//StartParallelUserWorkItem(i);
StartTask(i);
Thread.Sleep(100); //sleep a while so that the other thread is working
LogThreadCounts();
}
for (int i = 0; i < 40; i++)
{
Thread.Sleep(1100); //sleep a while so that the other thread is working
LogThreadCounts();
}
}
private void StartTask(int label)
{
var taskLabel = "Task " + label;
Console.WriteLine("Enqueue " + taskLabel);
Task.Run(() => GetResponseAwait(taskLabel));
}
private static void LogThreadCounts()
{
int worker;
int io;
ThreadPool.GetAvailableThreads(out worker, out io);
Console.WriteLine("Worker Threads Available:" + '\t' + worker + '\t' + "IO Threads Available:" + '\t' + io + '\t' +
"Threads held by Process: " + '\t' + Process.GetCurrentProcess().Threads.Count);
}
private void GetResponseSync(object label)
{
Console.WriteLine("Start Sync " + label);
try
{
var req = GetRequest();
using (var resp = req.GetResponse())
{
Console.WriteLine(resp.ContentLength);
}
}
catch (Exception e)
{
Console.WriteLine("Error response " + label);
}
Console.WriteLine("End response " + label);
}
private void BeginResponseAsync(object label)
{
Console.WriteLine("Start Async " + label);
try
{
var req = GetRequest();
req.BeginGetResponse(EndGetResponseAsync, req);
}
catch (Exception e)
{
Console.WriteLine("Error Async " + label);
}
}
private void EndGetResponseAsync(IAsyncResult result)
{
Console.WriteLine("Respond Async ");
var req = (WebRequest)result.AsyncState;
using (var resp = req.EndGetResponse(result))
{
Console.WriteLine(resp.ContentLength);
}
Console.WriteLine("End Async ");
}
private async Task GetResponseAwait(object label)
{
Console.WriteLine("Start Await " + label);
try
{
var req = GetRequest();
using (var resp = await req.GetResponseAsync())
{
Console.WriteLine(resp.ContentLength);
}
}
catch (Exception e)
{
Console.WriteLine("Error Await " + label);
}
Console.WriteLine("End Await " + label);
}
private WebRequest GetRequest()
{
var req = WebRequest.Create("http://aslowwebsite.com");
req.Timeout = (int)TimeSpan.FromSeconds(60).TotalMilliseconds;
return req;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.