2013-03-17 112 views
4

有人可以展示如何在不創建多個線程的情況下發出併發請求嗎?例如,我想要一個能夠發出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,所以有一些bug上述代碼作爲它正在並行8名的請求。最初它只能並行處理5個請求,但只要一個請求完成,它就會發出4個請求(應該只發送一個請求)。

不得不修復了一些bug,但是這一切現在工作了:

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(); 

如果有更好的方式來做到這一點,請讓我知道。

+0

「沒有創建多個線程」和「當線程發出異步請求時,同一個線程可以用於發出下一個請求」是衝突的語句。 – 2013-03-17 03:03:37

回答

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?

+0

謝謝。其有用的知道。我還沒有嘗試過。但是這不會在我的方法中創建8個線程與單線程?是否有一些監視工具可以知道應用程序創建的線程數量? – morpheus 2013-03-17 19:10:09

+0

不應該如果你的異步代碼被正確寫入,即不佔用線程。上面你自己的代碼基本上是Parallel.Invoke的重新實現。有一些性能計數器可以監視'.NET CLR LocksAndThreads'類別中OS線程和託管線程的數量。 – 2013-03-18 00:16:18

+0

好吧,試過了,這不起作用。你可以自己嘗試。會發生什麼情況是所有的異步調用一次會被解僱。 Parallel.Invoke(新ParallelOptions {MaxDegreeOfParallelism = concurrentRequests}, ()=> { 的foreach(VAR i的Enumerable.Range(0,maxRequests)) { tasks.Add(makeRequest的(URI)); } }); Task.WaitAll(tasks.ToArray()); – morpheus 2013-03-18 02:00:53

相關問題