C# 使用异步等待的并行多线程下载

C# 使用异步等待的并行多线程下载,c#,multithreading,async-await,C#,Multithreading,Async Await,我有100多个大文件要在我的windows服务-c#中从网上下载。要求是一次最多支持4次并行web文件下载 我可以使用async await实现并发/并行下载,还是必须使用BackgroundWorker进程或线程?异步等待是多线程的吗? 请参见下面使用async await的示例程序: static int i = 0; Timer_tick() { while(i < 4) { i++; model =

我有100多个大文件要在我的windows服务-c#中从网上下载。要求是一次最多支持4次并行web文件下载

我可以使用async await实现并发/并行下载,还是必须使用BackgroundWorker进程或线程?异步等待是多线程的吗? 请参见下面使用async await的示例程序:

    static int i = 0;

    Timer_tick()
    {
      while(i < 4)
      {
        i++;
        model = GetNextModel();
        await Download(model);
      }
    }

    private async Download(XYZ model)
    {
    Task<FilesetResult> t = DoWork(model);
    result = await t; 
    //Use Result
    }

    private async Task<FilesetResult> Work(XYZ model)
    {
    fileresult = await api.Download(model.path)
    i--;
    return filesetresult;
    }
static int i=0;
计时器(滴答声)
{
而(i<4)
{
i++;
model=GetNextModel();
等待下载(型号);
}
}
专用异步下载(XYZ型号)
{
任务t=工作(模型);
结果=等待t;
//使用结果
}
专用异步任务工作(XYZ模型)
{
fileresult=wait api.Download(model.path)
我--;
返回filesetresult;
}

您可以使用
信号量lim
类限制并行运行的异步任务的数量。比如:

List<DownloadRequest> requests = Enumerable.Range(0, 100).Select(x => new DownloadRequest()).ToList();
using (var throttler = new SemaphoreSlim(4))
{
    Task<DownloadResult>[] downloadTasks = requests.Select(request => Task.Run(async () =>
    {
        await throttler.WaitAsync();
        try
        {
            return await DownloadTaskAsync(request);
        }
        finally
        {
            throttler.Release();
        }
    })).ToArray();
    await Task.WhenAll(downloadTasks);
}
List requests=Enumerable.Range(01100)。选择(x=>newdownloadrequest()).ToList();
使用(var节流器=新信号量LIM(4))
{
任务[]下载任务=请求。选择(请求=>Task.Run(异步()=>
{
wait throttler.WaitAsync();
尝试
{
返回等待下载任务异步(请求);
}
最后
{
节流器释放();
}
})).ToArray();
等待任务。WhenAll(下载任务);
}
更新:感谢您的评论,修复了问题

Update2:动态请求列表的示例解决方案

public class DownloadManager : IDisposable
{
    private readonly SemaphoreSlim _throttler = new SemaphoreSlim(4);

    public async Task<DownloadResult> DownloadAsync(DownloadRequest request)
    {
        await _throttler.WaitAsync();
        try
        {
            return await api.Download(request);
        }
        finally
        {
            _throttler.Release();
        }
    }

    public void Dispose()
    {
        _throttler?.Dispose();
    }
}
公共类下载管理器:IDisposable
{
私有只读信号量lim _throttler=新信号量lim(4);
公共异步任务DownloadAsync(DownloadRequest请求)
{
wait_throttler.WaitAsync();
尝试
{
返回等待api下载(请求);
}
最后
{
_节流器释放();
}
}
公共空间处置()
{
_节流阀?.Dispose();
}
}

您可以使用
信号量lim
类限制并行运行的异步任务的数量。比如:

List<DownloadRequest> requests = Enumerable.Range(0, 100).Select(x => new DownloadRequest()).ToList();
using (var throttler = new SemaphoreSlim(4))
{
    Task<DownloadResult>[] downloadTasks = requests.Select(request => Task.Run(async () =>
    {
        await throttler.WaitAsync();
        try
        {
            return await DownloadTaskAsync(request);
        }
        finally
        {
            throttler.Release();
        }
    })).ToArray();
    await Task.WhenAll(downloadTasks);
}
List requests=Enumerable.Range(01100)。选择(x=>newdownloadrequest()).ToList();
使用(var节流器=新信号量LIM(4))
{
任务[]下载任务=请求。选择(请求=>Task.Run(异步()=>
{
wait throttler.WaitAsync();
尝试
{
返回等待下载任务异步(请求);
}
最后
{
节流器释放();
}
})).ToArray();
等待任务。WhenAll(下载任务);
}
更新:感谢您的评论,修复了问题

Update2:动态请求列表的示例解决方案

public class DownloadManager : IDisposable
{
    private readonly SemaphoreSlim _throttler = new SemaphoreSlim(4);

    public async Task<DownloadResult> DownloadAsync(DownloadRequest request)
    {
        await _throttler.WaitAsync();
        try
        {
            return await api.Download(request);
        }
        finally
        {
            _throttler.Release();
        }
    }

    public void Dispose()
    {
        _throttler?.Dispose();
    }
}
公共类下载管理器:IDisposable
{
私有只读信号量lim _throttler=新信号量lim(4);
公共异步任务DownloadAsync(DownloadRequest请求)
{
wait_throttler.WaitAsync();
尝试
{
返回等待api下载(请求);
}
最后
{
_节流器释放();
}
}
公共空间处置()
{
_节流阀?.Dispose();
}
}

手工操作似乎非常复杂

var files = new List<Uri>();

Parallel.ForEach(files, 
                 new ParallelOptions { MaxDegreeOfParallelism = 4 },
                 this.Download);

用手做这件事似乎非常复杂

var files = new List<Uri>();

Parallel.ForEach(files, 
                 new ParallelOptions { MaxDegreeOfParallelism = 4 },
                 this.Download);

我的downloadrequests列表是动态的,它随着时间的推移而增大或减小Task.Run返回的任务实例不等待,这意味着最终将立即命中(很可能是在DownloadTaskAsync仍在进行时),并且信号量将被释放。如果不将返回的任务添加到列表中并在某个时刻等待,此解决方案将是纯粹的开销-它不会做任何有意义的事情whatsoever@Gags添加了动态请求的解决方案,当多个客户端可以调用下载方法,但有些客户端会等到其他客户端完成时才调用。@Gags yes,第一个任务发布后,第五个任务将继续semaphore@gags使用.Wait()可以有效地阻止调用线程。如果您想并行使用,您应该有异步/等待调用者,或者您需要启动多个任务并立即等待它们(第一个代码示例)。我的downloadrequests列表是动态的,它随时间而增大或减小任务返回的任务实例。Run未被等待,这意味着最终将立即命中(很可能是在下载TaskAsync时)如果不将返回的任务添加到列表中并在某个时刻等待,这个解决方案将是纯粹的开销-它不会做任何有意义的事情whatsoever@Gags添加了动态请求的解决方案,当多个客户端可以调用下载方法,但有些客户端会等到其他客户端完成时才调用。@Gags yes,one将在首次任务发布后继续semaphore@gags使用.Wait()有效地意味着阻止调用线程。如果要并行使用,则应该有异步/等待调用程序,或者需要启动多个任务并同时等待它们(第一个代码示例)是的,但是你不必一个接一个地等待下载,而是在进入下一批之前启动你想要的数量并使用
任务。WaitAll
。但是它不是多线程的。它只是允许IO并行发生,但是所有CPU绑定的事情都将由一个线程处理。如果你有CPU意图如果IO是并行的,但只使用了一个线程,那么使用多个线程会提高性能吗?完全取决于除IO之外您还做了什么。是的,但您不必逐个等待下载,而是启动所需的数量并使用
T在进入下一批之前,请询问.WaitAll
。但它不是多线程的。它只是允许IO并行进行,但所有CPU绑定的内容都将由一个线程处理。如果您有CPU密集型代码