2017-12-13 9 views
2

많은 다른 웹 서비스를 동시에 호출하고 데이터를 집계하려고합니다.많은 웹 사이트에서 데이터를 비동기 적으로 가져 와서 컬렉션에 추가하려면 어떻게해야합니까?

웹 호출마다 태스크를 작성하고 각 태스크에 공유 컨테이너를 전달하고 각 호출의 데이터를 컨테이너에 저장하는 것이 나의 목적입니다. 각 웹 호출에서 공유 컨테이너로 데이터를 가져올 수 있다면 행복합니다.

내가 뭘 하려는지 예제를 만들었지 만 Task.WaitAll 줄의 예외와 함께 충돌하는 경우가 있습니다. "하나 이상의 오류가 발생했습니다 (소스 배열이 충분하지 않았습니다.) 소스 인덱스, 길이 및 배열의 ​​하한값 매개 변수 이름 : sourceArray) ".

async/await 및 multithreading을 처음 사용했습니다. 또한 작동 데이터를 추가 할 때

using System; 
using System.Collections.Generic; 
using System.Net.Http; 
using System.Threading.Tasks; 
using System.Linq; 

namespace ConsoleApp1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Console.WriteLine("Starting tasks..."); 
      List<Task> tasks = new List<Task>(); 
      List<char> container = new List<char>(); 
      for (int i = 0; i < 80; i++) 
      { 
       tasks.Add(LongTask(container)); 
      } 
      Task.WaitAll(tasks.ToArray()); 
      Console.WriteLine("Checkpoint 1."); 
      Console.WriteLine("Tasks Finished"); 

      Console.ReadLine(); 
     } 

     public static async Task<string> LongTask(List<char> container) 
     { 
      var client = new HttpClient(); 
      var text = await client.GetAsync("http://www.google.com"); 

      var myList = text.StatusCode.ToString().ToList(); 

      container.AddRange(myList); 

      return text.StatusCode.ToString(); 
     } 
    } 
} 
+2

목록 .AddRange()는 스레드로부터 안전하지 않습니다. ConcurrentBag와 같은 것을 사용해 보셨나요? –

+3

[복수 대기 대 Task.WaitAll - 이에 상응하는] 가능한 복제본 (https://stackoverflow.com/questions/32119507/multiple-awaits-vs-task-waitall-equivalent) – GolezTrol

+0

답변을 찾으십시오. –

답변

3

당신은 현재 목록 주위에 lock 구조를 사용할 수있다. 아래

이 당신의 방법

var myList = text.StatusCode.ToString().ToList(); 
lock(_lock) 
{ 
    container.AddRange(myList); 
} 

에 갈 것이다 또는 당신이 목록을 대체 코드에서 너무 ConcurrentBag

를 사용할 수있다 클래스 수준

private static object _lock = new object(); 

에 갈 것입니다 이것에 의해, 주된 방법

ConcurrentBag<string> container = new ConcurrentBag<string>(); 

및 LongTask (이하와 같이한다. 여기

var myList = text.StatusCode.ToString().ToList();    
container.AddRange(myList); 

ConcurrentBag doenst이 AddRange 방법을 가지고 내가 당신에게 문자열의 목록을 제안하고 당신이 할 수있는 것보다 ConcurrentBag에 추가 foreach 쓰기 않으면 기회가 다른 스레드는 한 번 그 대신에, 사이에 모두 완료 얻을 char를 추가 ConcurrentBag에서 string을 가져 와서 char 배열 또는 목록으로 변환 할 수 있습니다.

참고 :ConcurrentBag<T>은 동일한 스레드가 가방에 저장된 데이터를 생성하고 소비하는 시나리오에 최적화 된 스레드 안전 백 구현입니다.

+0

ConcurrentBag를 내부 구현으로 사용하면 스레드에 추가되는 스레드가 거의 항상 추가 된 항목을 검색하는 매우 특정한 문제를 해결할 수 있습니다. 당신이 말한 목록과 자물쇠는 더 효율적인 해결책입니다. –

+0

@RichardBlewett - 그냥 답장 해주세요. –