C# 以同步方式下载文件,并使用C异步提取下载的文件#

C# 以同步方式下载文件,并使用C异步提取下载的文件#,c#,asynchronous,async-await,.net-4.0,task-parallel-library,C#,Asynchronous,Async Await,.net 4.0,Task Parallel Library,我有一些大型zip文件的URL的大列表。我正在使用HttpClient循环下载文件。我必须在下载过程后提取文件。我希望在每个文件下载完成时开始提取它们,而不是等待整个下载过程完成。文件下载应该以同步方式(一个接一个)进行,提取应该与每个下载的文件异步进行。我的应用程序使用.NETFramework4.5.2和C#7 在下面的代码中,文件下载也是异步的。由于带宽问题,我正在尝试避免异步下载 public void DownloadAndExtract() { IDataReader

我有一些大型zip文件的URL的大列表。我正在使用
HttpClient
循环下载文件。我必须在下载过程后提取文件。我希望在每个文件下载完成时开始提取它们,而不是等待整个下载过程完成。文件下载应该以同步方式(一个接一个)进行,提取应该与每个下载的文件异步进行。我的应用程序使用.NETFramework4.5.2和C#7

在下面的代码中,文件下载也是异步的。由于带宽问题,我正在尝试避免异步下载

public void DownloadAndExtract()
{
        IDataReader dr = _myDB.GetFileUrl();
        while (dr.Read())
        {
                DownloadFile(new Uri(dr["URL"].ToString())).ContinueWith(task1 =>
                {
                      var downloadedFilePath = task1.Result.fileName;
                      ExtractFile(downloadedFilePath).GetAwaiter().GetResult();
                });
         }
         dr.Close();     
 }

我想我应该简化我的生活:

public async Task DownloadAndExtractAsync()
{
    using(IDataReader dr = _myDB.GetFileUrl()){
        while (dr.Read())
        {
          var f = await DownloadFileAsync(new Uri(dr["URL"].ToString()));
          _ = ExtractFileAsync(f.fileName);
        }
    }
}
唯一让我不安的是,它可能会将db连接保持在不合理的长时间打开状态。。也许:

public async Task DownloadAndExtractAsync()
{
    DataTable dt = new DataTable();
    using(IDataReader dr = _myDB.GetFileUrl())
       dt.Load(dr);

    foreach(DataRow dr in dt.Rows)
    {
        var f = await DownloadFileAsync(new Uri(dr["URL"].ToString()));
        _ = ExtractFileAsync(f.fileName);
    }
}
将以异步方式运行的方法作为后缀(使用
…Async
)将有利于必须阅读代码的人,尤其是那些在互联网上无法从调用方法的智能感知/定义中获益的人。使用两个块,一个用于下载URL,另一个用于提取文件

private void DownloadAndExtract()
{
    var downloadBlock = new TransformBlock<Uri, string>(async uri =>
    {
        var downloadedFile = await DownloadFileAsync(uri);
        return downloadedFile.fileName;
    }, new ExecutionDataflowBlockOptions()
    {
        MaxDegreeOfParallelism = 1
    });

    var extractBlock = new ActionBlock<string>(async filePath =>
    {
        await ExtractFileAsync(filePath);
    }, new ExecutionDataflowBlockOptions()
    {
        MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded
    });

    downloadBlock.LinkTo(extractBlock,
        new DataflowLinkOptions() { PropagateCompletion = true });

    IDataReader dr = _myDB.GetFileUrl();
    while (dr.Read())
    {
        downloadBlock.Post(new Uri(dr["URL"].ToString()));
    }
    dr.Close();
    downloadBlock.Complete();
    extractBlock.Completion.Wait();
}
private void下载和提取()
{
var downloadBlock=newtransformblock(异步uri=>
{
var downloadedFile=等待下载文件异步(uri);
返回downloadedFile.fileName;
},新的ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism=1
});
var extractBlock=newactionblock(异步文件路径=>
{
等待提取文件异步(文件路径);
},新的ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism=DataflowBlockOptions.Unbounded
});
downloadBlock.LinkTo(提取块,
新的DataflowLinkOptions(){PropagateCompletion=true});
IDataReader dr=_myDB.GetFileUrl();
while(dr.Read())
{
downloadBlock.Post(新的Uri(dr[“URL”].ToString());
}
Close博士();
downloadBlock.Complete();
extractBlock.Completion.Wait();
}
在将所有
Uri
发布到
downloadBlock
之前,将它们存储在一个列表中会更安全。对于上面的代码,数据库中一个格式错误的
URL
将导致
downloads和extract
方法失败,而以前的URL将在后台以一种“即发即弃”的方式下载和提取



注意:我在异步方法
DownloadFile
ExtractFile
中添加了
Async
后缀,以符合要求。

答案是肯定的。实现这一点的方法有很多,但是根据您的需要(细节非常简单),可以很容易地将其放入数据流管道或反应式扩展(RX),或者您可以在任务和信号量限制方面获得创造性,因为没有实际的细节或具体的示例工作流(除了有限的顶层概述),编写对您有用的代码几乎是不可能的
文件下载应该以同步方式(一个接一个)
为什么要同步?为了满足您的要求,一个线程/任务添加到
BlockingCollection
中,第二个线程/任务使用
并行。ForEach
处理
BlockingCollection
似乎是一种相当简单的方法。在提取完所有文件后,我必须执行一些其他操作。如何知道此代码中的所有提取任务都已完成?将ExtractFileAsync.ContinueWith放入?是否将操作作为摘录的一部分?您的要求中没有太多细节。下一步操作应仅在提取所有文件后开始(而不是在提取每个文件后)。所以它不能作为提取物的一部分。如何等待所有ExtractFileAsync完成?啊,在这种情况下,您可能希望将它们收集到某种列表中,并在其中使用Task.whalll。。我想这是有道理的,因为你想在提取之前等待下载,而且提取速度可能更快,它可以像在循环中进行下载和提取添加一样简单,然后在循环之外使用等待,然后继续。我要警惕的一件事可能是太多的同时提取可能会真正打击事情的IO方面;希望你至少有SSD!在这里,我想向ExtractFileAsync方法传递一个FileCategory。FileCategory在数据库中。如何将dr[“FileCategory”]传递给ExtractFileAsync方法?@rinesh如果您有更多的数据要从一个块传递到另一个块,那么最简单的方法可能是创建一个类,该类包含处理URL所需的所有数据的属性,以及一路上将获得的数据的属性。然后,假设类名为
UrlData
,可以像这样更改数据流管道中的块类型:
TransformBlock
ActionBlock
。然后,您将为管道提供此类的实例:
downloadBlock.Post(newurldata(){Uri:newuri(dr[“URL”].ToString())})