我正在尝试使用WebClient异步下载一堆文件。根据我的理解,这是可能的,但每次下载需要一个WebClient
对象。所以我想我只是在我的程序开始时把一堆它们放在一个队列中,然后一次一个地弹出它们并告诉它们下载一个文件。文件下载完成后,可以将它们推回到队列中。
把东西推到我的队列上应该不会太糟糕,我只需做类似的事情:
lock(queue) {
queue.Enqueue(webClient);
}
右?但是把它们弹出来呢?我希望我的主线程在队列为空时休眠(等到另一个Web客户端准备就绪,以便它可以开始下一次下载)。我想我可以在队列旁边使用Semaphore
来跟踪队列中有多少元素,这会让我的线程在必要时进入休眠状态,但这似乎不是一个很好的解决方案。如果每次推送/弹出我的队列中的某些内容并且它们不同步时我忘记减少/增加我的信号量会发生什么?那会很糟糕。是不是有一些很好的方法让queue.Dequeue()
自动睡觉,直到有一个项目出队然后继续?
我也欢迎完全不涉及队列的解决方案。我只想知道一个队列是跟踪哪些WebClient可以使用的最简单方法。
答案 0 :(得分:5)
以下是使用信号量的示例。 IMO比使用Monitor更清洁:
public class BlockingQueue<T>
{
Queue<T> _queue = new Queue<T>();
Semaphore _sem = new Semaphore(0, Int32.MaxValue);
public void Enqueue(T item)
{
lock (_queue)
{
_queue.Enqueue(item);
}
_sem.Release();
}
public T Dequeue()
{
_sem.WaitOne();
lock (_queue)
{
return _queue.Dequeue();
}
}
}
答案 1 :(得分:3)
您想要的是生产者/消费者队列。
我的线程教程中有simple example of this - 在该页面的一半左右滚动。它是在仿制前编写的,但它应该很容易更新。您可能需要添加各种功能,例如“停止”队列的能力:这通常通过使用一种“空工作项”令牌来执行;你在队列中注入尽可能多的“停止”项目,并且当它们出现一个线程时,每个项目都会停止出列。
搜索“生产者消费者队列”可能会为您提供更好的代码示例 - 这实际上就是展示等待/脉冲。
IIRC,.NET 4.0中有一些类型(作为Parallel Extensions的一部分)会做同样的事情,但更好:)我想你希望BlockingCollection包裹一个{ {3}}答案 2 :(得分:0)
我使用BlockingQueue来处理这种情况。当队列为空时,你可以调用.Dequeue,而调用线程只会等到有什么东西出队。
public class BlockingQueue<T> : IEnumerable<T>
{
private int _count = 0;
private Queue<T> _queue = new Queue<T>();
public T Dequeue()
{
lock (_queue)
{
while (_count <= 0)
Monitor.Wait(_queue);
_count--;
return _queue.Dequeue();
}
}
public void Enqueue(T data)
{
if (data == null)
throw new ArgumentNullException("data");
lock (_queue)
{
_queue.Enqueue(data);
_count++;
Monitor.Pulse(_queue);
}
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
while (true)
yield return Dequeue();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<T>) this).GetEnumerator();
}
}
只需使用它代替普通的队列,它就可以做你需要的。