C# 真实世界异步和等待代码示例

C# 真实世界异步和等待代码示例,c#,async-await,C#,Async Await,我一直在到处寻找.NET4.5中新的Async和Wait特性的好的现实例子。我想出了以下代码来下载文件列表并限制并发下载的数量。我将非常感谢任何改进/优化此代码的最佳实践或方法 我们使用下面的语句调用下面的代码 await this.asyncDownloadManager.DownloadFiles(this.applicationShellViewModel.StartupAudioFiles, this.applicationShellViewModel.SecurityCookie, s

我一直在到处寻找.NET4.5中新的Async和Wait特性的好的现实例子。我想出了以下代码来下载文件列表并限制并发下载的数量。我将非常感谢任何改进/优化此代码的最佳实践或方法

我们使用下面的语句调用下面的代码

await this.asyncDownloadManager.DownloadFiles(this.applicationShellViewModel.StartupAudioFiles, this.applicationShellViewModel.SecurityCookie, securityCookieDomain).ConfigureAwait(false);
然后,我们使用事件将下载的文件添加到ViewModel上的observablecollection(新的线程安全版本,在.NET4.5中)

public class AsyncDownloadManager
    {
        public event EventHandler<DownloadedEventArgs> FileDownloaded;

        public async Task DownloadFiles(string[] fileIds, string securityCookieString, string securityCookieDomain)
          {
            List<Task> allTasks = new List<Task>();
            //Limits Concurrent Downloads 
            SemaphoreSlim throttler = new SemaphoreSlim(initialCount: Properties.Settings.Default.maxConcurrentDownloads);

            var urls = CreateUrls(fileIds);

            foreach (var url in urls)   
            {  
                await throttler.WaitAsync();
                allTasks.Add(Task.Run(async () => 
                {
                    try
                    {
                        HttpClientHandler httpClientHandler = new HttpClientHandler();
                        if (!string.IsNullOrEmpty(securityCookieString))
                        {
                            Cookie securityCookie;
                            securityCookie = new Cookie(FormsAuthentication.FormsCookieName, securityCookieString);
                            securityCookie.Domain = securityCookieDomain;
                            httpClientHandler.CookieContainer.Add(securityCookie);    
                        }                     

                        await DownloadFile(url, httpClientHandler).ConfigureAwait(false);
                    }
                    finally
                    {
                        throttler.Release();
                    }
                }));
            }
            await Task.WhenAll(allTasks).ConfigureAwait(false);
        }

        async Task DownloadFile(string url, HttpClientHandler clientHandler)
        {
            HttpClient client = new HttpClient(clientHandler);
            DownloadedFile downloadedFile = new DownloadedFile();

            try
            {
                HttpResponseMessage responseMessage = await client.GetAsync(url).ConfigureAwait(false);
                var byteArray = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false);

                if (responseMessage.Content.Headers.ContentDisposition != null)
                {
                    downloadedFile.FileName = Path.Combine(Properties.Settings.Default.workingDirectory, responseMessage.Content.Headers.ContentDisposition.FileName);
                }
                else
                {
                    return;
                }

                if (!Directory.Exists(Properties.Settings.Default.workingDirectory))   
                {
                    Directory.CreateDirectory(Properties.Settings.Default.workingDirectory);
                }
                using (FileStream filestream = new FileStream(downloadedFile.FileName, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
                {
                    await filestream.WriteAsync(byteArray, 0, byteArray.Length);
                }
            }
            catch(Exception ex)
            {    
                return; 
            }
            OnFileDownloaded(downloadedFile);
        }

        private void OnFileDownloaded(DownloadedFile downloadedFile)
        {    
            if (this.FileDownloaded != null)
            {
                this.FileDownloaded(this, new DownloadedEventArgs(downloadedFile));
            }
        }    

    public class DownloadedEventArgs : EventArgs
    {
        public DownloadedEventArgs(DownloadedFile downloadedFile)
        {   
            DownloadedFile = downloadedFile;
        }

        public DownloadedFile DownloadedFile { get; set; }
    }
公共类异步下载管理器
{
已下载公共事件事件处理程序文件;
公共异步任务下载文件(字符串[]文件ID、字符串securityCookieString、字符串securityCookieDomain)
{
列出所有任务=新建列表();
//限制并发下载
SemaphoreSlim throttler=new SemaphoreSlim(initialCount:Properties.Settings.Default.maxConcurrentDownloads);
var url=createurl(fileid);
foreach(url中的变量url)
{  
wait throttler.WaitAsync();
allTasks.Add(Task.Run)(异步()=>
{
尝试
{
HttpClientHandler HttpClientHandler=新的HttpClientHandler();
如果(!string.IsNullOrEmpty(securityCookieString))
{
Cookie securityCookie;
securityCookie=新Cookie(FormsAuthentication.FormScookeName,securityCookieString);
securityCookie.Domain=securityCookieDomain;
httpClientHandler.CookieContainer.Add(securityCookie);
}                     
等待下载文件(url,httpClientHandler);
}
最后
{
节流器释放();
}
}));
}
等待任务.WhenAll(所有任务).配置等待(false);
}
异步任务下载文件(字符串url,HttpClientHandler-clientHandler)
{
HttpClient=新的HttpClient(clientHandler);
DownloadedFile DownloadedFile=新的DownloadedFile();
尝试
{
HttpResponseMessage responseMessage=await client.GetAsync(url).ConfigureAwait(false);
var byteArray=await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
if(responseMessage.Content.Headers.ContentDisposition!=null)
{
downloaddedfile.FileName=Path.Combine(Properties.Settings.Default.workingDirectory,responseMessage.Content.Headers.ContentDisposition.FileName);
}
其他的
{
返回;
}
如果(!Directory.Exists(Properties.Settings.Default.workingDirectory))
{
CreateDirectory(Properties.Settings.Default.workingDirectory);
}
使用(FileStream FileStream=new FileStream(downloadedFile.FileName,FileMode.Create,FileAccess.Write,FileShare.None,bufferSize:4096,useAsync:true))
{
等待filestream.WriteAsync(byteArray,0,byteArray.Length);
}
}
捕获(例外情况除外)
{    
返回;
}
onfiledownload(下载文件);
}
私有void onfiledownload(DownloadedFile DownloadedFile)
{    
如果(this.FileDownloaded!=null)
{
this.FileDownloaded(this,newdownloadedeventargs(downloadedFile));
}
}    
公共类已下载EventArgs:EventArgs
{
公共下载事件目标(下载文件下载文件)
{   
DownloadedFile=DownloadedFile;
}
公共下载文件下载文件{get;set;}
}
根据Svick的建议-以下是直接问题:

  • 在其他Async/Await方法中嵌入Async/Await有什么影响?(将文件流写入Async/Await方法中的磁盘)
  • httpclient应该用于每个单独的任务,还是应该共享一个单独的任务
  • 事件是将下载的文件引用“发送”到viewmodel的好方法吗? [我还将在codereview中发布]

  • 如果嵌入async Wait,则应使用

    Task.ConfigureAwait(false)
    

    在任何返回任务的情况下,否则该任务将在调用者的线程上下文上继续,除了在UI线程上,这是不必要的。总之,库应该使用ConfigureAwait(false),UI代码不应该使用。就是这样!

    我认为您的问题与您的代码没有直接关系,因此我将在这里回答他们:

    在其他Async/Await方法中嵌入Async/Await有什么效果?(将文件流写入Async/Await方法中的磁盘。)

    async
    方法就是这样组合的。实际上,只有
    async
    -
    wait
    可以用来:组合异步方法以创建另一个异步方法

    发生的情况是,如果
    等待
    尚未完成的
    任务
    ,您的方法实际上会返回调用方。然后,当
    任务
    完成时,您的方法会在原始上下文(例如UI应用程序中的UI线程)上恢复

    如果您不想在原始上下文上继续(因为您不需要它),您可以使用
    ConfigureAwait(false)
    (就像您已经做的那样)来更改它。在
    Task.Run()
    中不需要这样做,因为该代码不在原始上下文中运行

    httpclient应该用于每个单独的任务,还是应该