C# 同时下载尽可能多的文件
我想从随机生成的URI下载图像,我发现最快的方式是不可阻挡的 我正在使用预生成的C# 同时下载尽可能多的文件,c#,asynchronous,parallel-processing,C#,Asynchronous,Parallel Processing,我想从随机生成的URI下载图像,我发现最快的方式是不可阻挡的 我正在使用预生成的列表的URI,达到大约400imgs/分钟(大约是使用标准线程时的8倍),但我希望它能够持续生成URI并下载新图像,直到我说暂停为止。如何做到这一点 private void StartButton_Click(object sender, EventArgs e) { List<string> ImageURI; GenerateURIs(out ImageURI); // create
列表
的URI,达到大约400imgs/分钟(大约是使用标准线程时的8倍),但我希望它能够持续生成URI并下载新图像,直到我说暂停为止。如何做到这一点
private void StartButton_Click(object sender, EventArgs e)
{
List<string> ImageURI;
GenerateURIs(out ImageURI); // creates list of 1000 uris
ImageNames.AsParallel().WithDegreeOfParallelism(50).Sum(s => DownloadFile(s));
}
private int DownloadFile(string URI)
{
try
{
HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(URI);
webrequest.Timeout = 10000;
webrequest.ReadWriteTimeout = 10000;
webrequest.Proxy = null;
webrequest.KeepAlive = false;
HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse();
using (Stream sr = webrequest.GetResponse().GetResponseStream())
{
DownloadedImages++;
using (MemoryStream ms = new MemoryStream())
{
sr.CopyTo(ms);
byte[] ImageBytes = ms.ToArray();
if (ImageBytes.Length == 503)
{
InvalidImages++;
return 0;
}
else
{
ValidImages++;
using (var Writer = new BinaryWriter(new FileStream("images/" + (++FilesIndex).ToString() + ".png", FileMode.Append, FileAccess.Write)))
{
Writer.Write(ImageBytes);
}
}
}
}
}
catch (Exception e)
{
return 0;
}
return 0;
}
private void StartButton_单击(对象发送方,事件参数e)
{
列出ImageURI;
GenerateURIs(out-ImageURI);//创建1000个uri的列表
ImageNames.AsParallel().WithDegreeOfParallelism(50).Sum(s=>DownloadFile(s));
}
私有int下载文件(字符串URI)
{
尝试
{
HttpWebRequest webrequest=(HttpWebRequest)webrequest.Create(URI);
webrequest.Timeout=10000;
webrequest.ReadWriteTimeout=10000;
webrequest.Proxy=null;
webrequest.KeepAlive=false;
HttpWebResponse webresponse=(HttpWebResponse)webrequest.GetResponse();
使用(Stream sr=webrequest.GetResponse().GetResponseStream())
{
下载图像++;
使用(MemoryStream ms=new MemoryStream())
{
高级文员(ms);
byte[]ImageBytes=ms.ToArray();
如果(ImageBytes.Length==503)
{
无效图像++;
返回0;
}
其他的
{
ValidImages++;
使用(var Writer=new BinaryWriter(新文件流(“images/”+(++fileindex.ToString()+“.png”、FileMode.Append、FileAccess.Write)))
{
Writer.Write(ImageBytes);
}
}
}
}
}
捕获(例外e)
{
返回0;
}
返回0;
}
您正在寻找的是一个生产者/消费者模型,在该模型中,生产者将项目添加到队列中,消费者从队列中提取项目BlockingCollection
使这非常容易。创建一个BlockingCollection
,让您的制作人在您生成项目时继续向其中添加项目,完成后调用completedadding
,并让您的消费者使用GetConsumingEnumerable
,您可以调用该可枚举项上的确切代码
您需要将生产者代码和消费代码都移动到非UI线程中,这样它们就不会阻塞UI,并且可以并行地生成/消费数据
还要注意,当前在
DownloadFile
方法中,您正在修改和访问实例数据,尽管这个方法可能会从不同的线程并发调用。像递增索引这样的操作是不安全的,因为它不是一个原子操作,这会导致代码产生可能的副作用。您需要避免在这些不同的线程之间使用共享状态,或者正确地同步对该共享状态的访问。首先,您当前的代码不是线程安全的<代码>无效图像,下载图像
和有效图像
都需要同步
也就是说,使用异步而不是线程可以更有效地完成这项工作。由于本例中几乎所有的“工作”都是IO绑定的,因此异步可能是一种更好、更具可扩展性的方法
请尝试以下方法:
private async void StartButton_Click(object sender, EventArgs e)
{
List<string> ImageURI;
GenerateURIs(out ImageURI); // creates list of 1000 uris
var requests = ImageURI
.Select(uri => (new WebClient()).DownloadDataTaskAsync(uri))
.Select(SaveImageFile);
await Task.WhenAll(requests);
}
private Task SaveImageFile(Task<byte[]> data)
{
try
{
byte[] ImageBytes = await data;
DownloadedImages++;
if (ImageBytes.Length == 503)
{
InvalidImages++;
return;
}
ValidImages++;
using (var file = new FileStream("images/" + (++FilesIndex).ToString() + ".png", FileMode.Append, FileAccess.Write))
{
await Writer.WriteAsync(ImageBytes, 0, ImageBytes.Length);
}
}
catch (Exception e)
{
}
return;
}
private async void StartButton_单击(对象发送方,事件参数e)
{
列出ImageURI;
GenerateURIs(out-ImageURI);//创建1000个uri的列表
var请求=ImageURI
.Select(uri=>(新WebClient()).DownloadDataTaskAsync(uri))
.选择(保存图像文件);
等待任务。何时(请求);
}
专用任务SaveImageFile(任务数据)
{
尝试
{
byte[]ImageBytes=等待数据;
下载图像++;
如果(ImageBytes.Length==503)
{
无效图像++;
返回;
}
ValidImages++;
使用(var file=newfilestream(“images/”+(++fileindex.ToString()+“.png”、FileMode.Append、FileAccess.Write))
{
wait Writer.WriteAsync(ImageBytes,0,ImageBytes.Length);
}
}
捕获(例外e)
{
}
返回;
}
请注意,使用async/await,您不再需要担心同步问题,因为这些值仍将在主UI线程上设置
至于暂停,有多种选择-您可以添加一个是否继续执行数据的标志,或者使用
CancellationTokenSource
在整个操作过程中提供取消支持。除了GUI阻塞之外,您还有其他问题吗,因为如果你只想知道如何在不阻塞UI的情况下完成一些工作,那么有大量的资源可以向你展示如何做到这一点。如果你有任何其他问题,你需要解释它们是什么。我的意思是别的,我已经发了帖子。我希望能够停止整个下载过程,并运行它更动态,而不是从预定义的列表。(正如我在第一句话后提到的)这看起来很聪明,我稍后会尝试一下,但是如何让它持续下载,而不是从预定义列表中下载?@user2843974只是这些方法的输入是如何生成的。如果不知道URI是如何创建的,就不可能回答这个问题……这只是站点名称+后面的随机字符串slash@user2843974你只需做一个循环,下载一些图片(里面有wait),然后用一个取消令牌停止。因为它使用异步,所以它永远不会阻塞…它对我来说工作得非常完美。我以前尝试过异步,但我无法做到这一点,因为我仍然不能真正理解异步。非常感谢。