假设有很多的下载需要处理。
var urls = new []{
"https://..."
};
var client = new HttpClient();
foreach(var url in urls)
{
var html = await client.GetStringAsync(url);
}
如果想并行处理的话,通常的做法是为每一个地址下载配上一个线程,然后等到所有线程处理结束。如果只想用4个线程呢?
var maxThreads=4;
//先把字符串放队列里
var q = new ConcurrentQueue<string>(urls);
var tasks = new List<Task>();
for(int n = 0; n < maxThreads;n++){
task.Add(Task.Run(async ()=>{
while(q.TryDequeue(out string url)){
var html = await client.GetStringAsync(url);
}
}));
}
await Tas.WhenAll(tasks);
以上先把字符串放队列里,然后规定线程的数量,这个很好。但是,有可能遍历完线程之后,队列里还有数据没有读出来。
来看看下面的做法。
var maxThread = 4;
var allTasks = new List<Task>();
var throtter = new SemaphoreSlim(iniitalCount:maxThreads);
foreach(var url in urls)
{
await throtter.WaitAsync();
allTasks.Add(Task.Run(()=> {}
try
{
var html = await client.GetStringAsync(url);
}
finally
{
throttler.Release();
}
));
}
await Task.WhenAll(allTasks);
还可以用Parallel.ForEach
var options = new PrallelOptions(){MaxDegreeOfParallelism = maxThreds};
Prallel.ForEach(urls, options, url => {
var html = client.GetStringAsync(url).Result;
})
第三个参数是Action
,只能是同步方法,如果想要使用异步方法,还有一个AsyncEnumerator
这个NuGet组件可以用。
await uris.PralleForEachAsync(
async url => {
var html = await httpClient.GetStringAsync(url);
}, maxDegreeOfParalellism:maxThreads
);
还有一种隔离策略,就是使用有限的并发数生成一个无限的排队队列。
var bulkhead = Policy.BulkheadAsync(maxThreds, Int32.MaxValue);
var tasks = new List<Task>();
foreach(var url in urls)
{
var t = builkhead.ExecuteAsync(async () => {
var html = await client.GetStringAsync(url);
});
tasks.Add(t);
}
await Task.WhenAll(tasks);