C# 并行处理许多HTTP web请求的好方法是什么?
我正在构建一个通用的URI检索系统。本质上,有一个泛型类C# 并行处理许多HTTP web请求的好方法是什么?,c#,multithreading,.net-4.0,C#,Multithreading,.net 4.0,我正在构建一个通用的URI检索系统。本质上,有一个泛型类Retriever,它维护一个要检索的URI队列。它有一个单独的线程,可以尽可能快地处理该队列。问题标题中指出的URI类型的一个示例是HTTP类型URI 问题是,当我开始请求通过抽象方法T RetrieveResource(Uri位置)检索资源时,由于缺乏异步性,它的速度会减慢 我的第一个想法是将RetrieveResource的返回类型更改为Task。然而,当我们有数千个未完成的任务时,这似乎会使任务堆积起来并导致许多问题。它似乎创建了许
Retriever
,它维护一个要检索的URI队列。它有一个单独的线程,可以尽可能快地处理该队列。问题标题中指出的URI类型的一个示例是HTTP类型URI
问题是,当我开始请求通过抽象方法T RetrieveResource(Uri位置)
检索资源时,由于缺乏异步性,它的速度会减慢
我的第一个想法是将RetrieveResource
的返回类型更改为Task
。然而,当我们有数千个未完成的任务时,这似乎会使任务堆积起来并导致许多问题。它似乎创建了许多实际的线程,而不是使用线程池。我想这会让一切都慢下来,因为一次发生的事情太多了,所以没有任何事情能取得重大进展
预计我们将有大量排队项目要检索,并且无法像排队时那样快速处理这些项目。随着时间的推移,系统有机会迎头赶上;但这肯定不快
我也考虑过不用维护队列和线程来处理它。。。仅在线程池
上将工作项排队。但是,如果说我需要在处理所有工作项之前关闭系统,或者以后需要考虑优先级或其他事情,我不确定这是否理想
我们还知道,检索资源是一个耗时的过程(0.250-5秒),但不一定是一个资源密集型过程。我们很好地将此并行化为数百个请求
我们的要求是:
- URI可以从任何线程排队,即使系统正在处理队列时也是如此
- 以后可能需要对检索进行优先级排序
- 检索应该能够暂停
- 当没有检索到任何内容时,应该发生最小的旋转(
在这里很有用)BlockingCollection
public abstract class Retriever<T> : IRetriever<T>, IDisposable
{
private readonly Thread worker;
private readonly BlockingCollection<Uri> pending;
private volatile int isStarted;
private volatile int isDisposing;
public event EventHandler<RetrievalEventArgs<T>> Retrieved;
protected Retriever()
{
this.worker = new Thread(this.RetrieveResources);
this.pending = new BlockingCollection<Uri>(new ConcurrentQueue<Uri>());
this.isStarted = 0;
this.isDisposing = 0;
}
~Retriever()
{
this.Dispose(false);
}
private void RetrieveResources()
{
while (this.isDisposing == 0)
{
while (this.isStarted == 0)
{
Monitor.Wait(this.pending);
}
Uri location = this.pending.Take();
// This is what needs to be concurrently done.
// In this example, it's synchronous, but just on a separate thread.
T result = this.RetrieveResource(location);
// At this point, we would fire our event with the retrieved data
}
}
protected abstract T RetrieveResource(Uri location);
protected void Dispose(bool disposing)
{
if (Interlocked.CompareExchange(ref this.isDisposing, 1, 0) == 1)
{
return;
}
if (disposing)
{
this.pending.CompleteAdding();
this.worker.Join();
}
}
public void Add(Uri uri)
{
try
{
this.pending.Add(uri);
}
catch (InvalidOperationException)
{
return;
}
}
public void AddRange(IEnumerable<Uri> uris)
{
foreach (Uri uri in uris)
{
try
{
this.pending.Add(uri);
}
catch (InvalidOperationException)
{
return;
}
}
}
public void Start()
{
if (Interlocked.CompareExchange(ref this.isStarted, 1, 0) == 1)
{
throw new InvalidOperationException("The retriever is already started.");
}
if (this.worker.ThreadState == ThreadState.Unstarted)
{
this.worker.Start();
}
Monitor.Pulse(this.pending);
}
public void Stop()
{
if (Interlocked.CompareExchange(ref this.isStarted, 0, 1) == 0)
{
throw new InvalidOperationException("The retriever is already stopped.");
}
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
}
公共抽象类检索器:IRetriever,IDisposable
{
私有只读线程工作者;
私有只读阻止收集挂起;
私有化开始;
私有易失性int isDisposing;
检索到公共事件事件处理程序;
受保护的检索器()
{
this.worker=新线程(this.RetrieveResources);
this.pending=new BlockingCollection(new ConcurrentQueue());
this.isStarted=0;
此.isDisposing=0;
}
~Retriever()
{
本.处置(假);
}
私有无效检索资源()
{
while(this.isDisposing==0)
{
while(this.isStarted==0)
{
Monitor.Wait(this.pending);
}
Uri location=this.pending.Take();
//这是需要同时做的事情。
//在本例中,它是同步的,但只是在一个单独的线程上。
T结果=此.RetrieveResource(位置);
//此时,我们将使用检索到的数据触发事件
}
}
受保护的抽象T检索资源(Uri位置);
受保护的无效处置(bool处置)
{
if(联锁比较交换(参考此isDisposing,1,0)==1)
{
返回;
}
如果(处置)
{
this.pending.CompleteAdding();
this.worker.Join();
}
}
公共无效添加(Uri)
{
尝试
{
this.pending.Add(uri);
}
捕获(无效操作异常)
{
返回;
}
}
公共void AddRange(IEnumerable URI)
{
foreach(Uri中的Uri)
{
尝试
{
this.pending.Add(uri);
}
捕获(无效操作异常)
{
返回;
}
}
}
公开作废开始()
{
if(联锁比较交换(参考this.isStart,1,0)==1)
{
抛出新的InvalidOperationException(“检索器已启动”);
}
if(this.worker.ThreadState==ThreadState.Unstarted)
{
this.worker.Start();
}
监视器。脉冲(此。待定);
}
公共停车场()
{
if(联锁比较交换(参考this.isStarted,0,1)=0)
{
抛出新的InvalidOperationException(“检索器已停止”);
}
}
公共空间处置()
{
这个。处置(真实);
总干事(本);
}
}
以上面的例子为基础。。。我认为这一解决方案增加了太多的复杂性,或者更确切地说,是奇怪的代码。。。就是这个
private void RetrieveResources()
{
while (this.isDisposing == 0)
{
while (this.isStarted == 0)
{
Monitor.Wait(this.pending);
}
Uri location = this.pending.Take();
Task<T> task = new Task<T>((state) =>
{
return this.RetrieveResource(state as Uri);
}, location);
task.ContinueWith((t) =>
{
T result = t.Result;
RetrievalEventArgs<T> args = new RetrievalEventArgs<T>(location, result);
EventHandler<RetrievalEventArgs<T>> callback = this.Retrieved;
if (!Object.ReferenceEquals(callback, null))
{
callback(this, args);
}
});
task.Start();
}
}
private void RetrieveResources()
{
while(this.isDisposing==0)
{
while(this.isStarted==0)
{
Monitor.Wait(this.pending);
}
Uri location=this.pending.Take();
任务任务=新任务((状态)=>
{
返回此.RetrieveResource(状态为Uri);
},地点);
任务。继续((t)=>
{
T结果=T.结果;
RetrievalEventArgs args=新的RetrievalEventArgs(位置、结果);
EventHandler callback=this.received;
如果(!Object.ReferenceEquals(回调,null))
{
回调(this,args);
}
});
task.Start();
}
}
我想我已经想出了一个很好的解决方案。我提取了两种方法
public abstract class Retriever<T> : IRetriever<T>
{
private readonly object locker;
private readonly BlockingCollection<Uri> pending;
private readonly Thread[] threads;
private CancellationTokenSource cancellation;
private volatile int isStarted;
private volatile int isDisposing;
public event EventHandler<RetrieverEventArgs<T>> Retrieved;
protected Retriever(int concurrency)
{
if (concurrency <= 0)
{
throw new ArgumentOutOfRangeException("concurrency", "The specified concurrency level must be greater than zero.");
}
this.locker = new object();
this.pending = new BlockingCollection<Uri>(new ConcurrentQueue<Uri>());
this.threads = new Thread[concurrency];
this.cancellation = new CancellationTokenSource();
this.isStarted = 0;
this.isDisposing = 0;
this.InitializeThreads();
}
~Retriever()
{
this.Dispose(false);
}
private void InitializeThreads()
{
for (int i = 0; i < this.threads.Length; i++)
{
Thread thread = new Thread(this.ProcessQueue)
{
IsBackground = true
};
this.threads[i] = thread;
}
}
private void StartThreads()
{
foreach (Thread thread in this.threads)
{
if (thread.ThreadState == ThreadState.Unstarted)
{
thread.Start();
}
}
}
private void CancelOperations(bool reset)
{
this.cancellation.Cancel();
this.cancellation.Dispose();
if (reset)
{
this.cancellation = new CancellationTokenSource();
}
}
private void WaitForThreadsToExit()
{
foreach (Thread thread in this.threads)
{
thread.Join();
}
}
private void ProcessQueue()
{
while (this.isDisposing == 0)
{
while (this.isStarted == 0)
{
Monitor.Wait(this.locker);
}
Uri location;
try
{
location = this.pending.Take(this.cancellation.Token);
}
catch (OperationCanceledException)
{
continue;
}
T data;
try
{
data = this.Retrieve(location, this.cancellation.Token);
}
catch (OperationCanceledException)
{
continue;
}
RetrieverEventArgs<T> args = new RetrieverEventArgs<T>(location, data);
EventHandler<RetrieverEventArgs<T>> callback = this.Retrieved;
if (!Object.ReferenceEquals(callback, null))
{
callback(this, args);
}
}
}
private void ThowIfDisposed()
{
if (this.isDisposing == 1)
{
throw new ObjectDisposedException("Retriever");
}
}
protected abstract T Retrieve(Uri location, CancellationToken token);
protected virtual void Dispose(bool disposing)
{
if (Interlocked.CompareExchange(ref this.isDisposing, 1, 0) == 1)
{
return;
}
if (disposing)
{
this.CancelOperations(false);
this.WaitForThreadsToExit();
this.pending.Dispose();
}
}
public void Start()
{
this.ThowIfDisposed();
if (Interlocked.CompareExchange(ref this.isStarted, 1, 0) == 1)
{
throw new InvalidOperationException("The retriever is already started.");
}
Monitor.PulseAll(this.locker);
this.StartThreads();
}
public void Add(Uri location)
{
this.pending.Add(location);
}
public void Stop()
{
this.ThowIfDisposed();
if (Interlocked.CompareExchange(ref this.isStarted, 0, 1) == 0)
{
throw new InvalidOperationException("The retriever is already stopped.");
}
this.CancelOperations(true);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
}