如何在不创建多个线程的情况下进行并发请求

时间:2013-03-17 02:52:31

标签: c#

有人可以展示如何在不创建多个线程的情况下发出并发请求吗?例如,我想要一个可以发出100个Web请求的程序,我不希望任何时候有超过8个并发请求。我不想为8个并发请求创建8个线程。当一个线程发出异步请求时,可以使用相同的线程来发出下一个请求,依此类推。我很抱歉,但我无法解决这个问题,并希望看到最好的解决方案。如果不清楚,我所说的请求是异步的。我希望看到一个不使用任何锁的解决方案,并使用内置类来完成工作。

这是我提出的一些代码,但它没有做它应该做的事情。

Task.Run(async () =>
                        {
                            var outstandingRequests = 0;
                            var requestCount = 0;
                            var tasks = new List<Task>(concurrentRequests);
                            while (requestCount < maxRequests)
                            {
                                if (outstandingRequests < concurrentRequests)
                                {
                                    tasks.Add(svc.GetDataAsync());  // a method that makes an async request
                                    Interlocked.Increment(ref outstandingRequests);
                                }
                                else
                                {
                                    var t = await Task.WhenAny(tasks);                                                                       
                                    Interlocked.Decrement(ref outstandingRequests);
                                    Interlocked.Increment(ref requestCount);
                                }
                            }
                            await Task.WhenAll(tasks);
                        }).Wait();

输出:

[] 1 Sending Request...Received Response 490,835.00 bytes in 15.6 sec
[] 2 Sending Request...
[] 3 Sending Request...
[] 4 Sending Request...
[] 5 Sending Request...
[] 6 Sending Request...
[] 7 Sending Request...
[] 8 Sending Request...
[] 9 Sending Request...

我已将concurrentRequests设置为5,因此上述代码中存在一些错误,因为它并行生成8个请求。最初它只同时发出了5个请求,但是只要一个请求完成,它就会再发出4个请求(应该只发出一个请求)。

不得不修复一些错误,但现在一切都解决了:

Task.Run(async () =>
                        {
                            var outstandingRequests = 0;
                            var requestCount = 0;
                            // adding and removing from a List<> at the same time is not thread-safe,
                            // so have to use a SynchronizedCollection<>
                            var tasks = new SynchronizedCollection<Task>();
                            while (requestCount < maxRequests)
                            {
                                if (outstandingRequests < concurrentRequests)
                                {
                                    tasks.Add(svc.GetDataAsync(uri)); // this will be your method that makes async web call and returns a Task to signal completion of async call
                                    Interlocked.Increment(ref outstandingRequests);
                                    Interlocked.Increment(ref requestCount);
                                }
                                else
                                {                                    
                                    **tasks.Remove(await Task.WhenAny(tasks));**
                                    Interlocked.Decrement(ref outstandingRequests);                                    
                                }
                            }
                            await Task.WhenAll(tasks);
                        }).Wait();

如果有更好的方法,请告诉我。

2 个答案:

答案 0 :(得分:4)

看起来你正在尝试重新发明线程池。不要这样做 - 只需使用现有功能:http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx
或者您可以使用请求方法的异步版本 - 它们也基于线程池。

答案 1 :(得分:1)

这个怎么样:

Parallel.Invoke (new ParallelOptions { MaxDegreeOfParallelism = 8 },
    svcs.Select (svc => svc.GetDataAsync ()).ToArray ()) ;

有一个有限并发任务调度程序here的Microsoft实现示例。请参阅SO问题System.Threading.Tasks - Limit the number of concurrent Tasks.Net TPL: Limited Concurrency Level Task scheduler with task priority?