2010-05-06 6 views
0

현재 HttpWebRequest/Response를 사용하는 멀티 스레드 다운로더 클래스가 있습니다. 모든 작품은 괜찮아요, 그것은 슈퍼 빠른지만, 문제는 데이터가 다른 애플 리케이션에 다운로드하는 동안 스트리밍해야한다는 것입니다. 이는 올바른 순서로 스트리밍되어야 함을 의미합니다. 즉, 첫 번째 청크를 먼저 처리 한 후 대기열에서 다음 처리를 수행해야합니다. 현재 내 다운로더 클래스는 sync이고 Download()는 byte []를 반환합니다. 내 비동기 멀티 스레드 클래스에서 예를 들어 4 개의 빈 요소 (슬롯 용)로 목록을 만들고 다운로드() 함수를 사용하여 각 스레드에 슬롯의 각 인덱스를 전달합니다. 그것은 동기화를 시뮬레이트하지만, 내가 필요한 것은 아닙니다. 첫 번째 청크가 다운로드되자 마자 데이터가 스트리밍되도록하려면 대기열을 어떻게 처리해야합니까?C# 질문에 멀티 스레드 다운로더

답변

2

를 귀하의 질문에 첫 번째 덩어리를 다운로드하고 첫 번째 덩어리를 사용할 준비가되었을 때, 스레드 당 이벤트를 사용하여 추적하는 스레드를 확인하는 방법에 대한 경우 어떤 스레드에 할당했는지 확인합니다. 첫 번째 스레드 (데이터의 첫 번째 청크 다운로드), 두 번째 스레드 (두 번째 데이터 청크)에 전달하는 이벤트 등을 전달하는 이벤트를 추적하십시오. 주 스레드 또는 다른 백그라운드 스레드 (UI 스레드를 차단하지 않으려면) 첫 번째 이벤트를 기다립니다. 첫 번째 스레드가 청크 다운로드를 완료하면 첫 번째 스레드는 첫 번째 이벤트를 설정/신호합니다. 대기중인 스레드는 깨어나서 첫 번째 데이터 청크를 사용할 수 있습니다.

다른 다운로드 스레드가 동일한 작업을 수행 할 수 있으며 완료되면 해당 이벤트를 알립니다. 아무도 기다리지 않아도 이벤트가 계속 표시되도록 수동 재설정 이벤트를 사용하십시오. 순서대로 데이터 블록이 필요한 스레드가 첫 번째 데이터 블록 처리를 완료하면 두 번째 이벤트를 기다릴 수 있습니다. 두 번째 이벤트가 이미 신호화된 경우 대기는 즉시 반환되고 스레드는 두 번째 데이터 블록 처리를 시작할 수 있습니다.

매우 큰 다운로드의 경우 라운드 로빈 방식으로 이벤트와 스레드를 재사용 할 수 있습니다. 데이터 청크를 소비하는 스레드가 순서대로 해당 이벤트를 소비하고 각 이벤트를 순서대로 대기하는 동안 완료되는 순서는 중요하지 않습니다.

당신이 똑똑하고 조심 스러우면 다음과 같이 하나의 이벤트 만 사용하여이 모든 작업을 수행 할 수 있습니다. 데이터 청크 포인터/객체의 초기 값을 Null로 설정하고 작업자 스레드가 데이터 청크를 다운로드하고 완성 된 청크를 지정합니다 글로벌 어레이의 해당 슬롯으로 전송 한 다음 공유 이벤트를 신호로 보냅니다. 소비자 스레드는 데이터 청크 카운터를 유지하여 다음에 처리해야하는 데이터 청크를 알고 공유 이벤트를 기다리고 신호가있을 때 전역 배열의 다음 슬롯을보고 데이터가 나타나는지 확인합니다. 계속해서 다음 슬롯에 데이터가없는 경우 소비자 스레드는 이벤트 대기시 다시 대기합니다. 또한 작업자 스레드가 다음에 다운로드해야하는 데이터 블록을 알 수있는 방법이 필요합니다 - 뮤텍스로 보호되거나 인터록 된 add/exchange를 사용하여 액세스되는 전역 카운터로 충분합니다. 각 작업자 스레드는 전역 카운터를 증가시키고 해당 데이터 청크 번호를 다운로드하고 결과를 데이터 청크의 전역 목록에있는 n 번째 슬롯에 할당합니다.

+0

글쎄, 내 코드는 다음과 같습니다. http : // pastie.org/949929 SingleChunk (index, startPos, endPos)를 다운로드하고 지정된 범위를 얻으면 chunkOutput 목록 에 기록합니다. 나는 카운터를 사용하는 방법을 생각했지만, 그것은 내 메인 스레드 또는 다른 작업 스레드를 반복해야한다는 것을 의미합니다. 그게 맞습니까? – blez

+0

다운로드 용량이 몇 메가 바이트보다 큰 경우, 예를 들어 다운로드 할 블록을 더 많이 선택하도록 각 작업자 스레드를 반복해야합니다. 스레드 수를 상당히 적게 유지하고 각 다운로드 청크의 크기가 너무 커지는 것을 막기를 원합니다. 매우 큰 데이터 청크를 다운로드하면 패킷 손실이나 전송 오류가 발생할 위험이 높아져 전체 청크를 다시 시작해야 할 수 있습니다. 1 ~ 2 분 이내에 전송할 수있는 작은 블록은 네트워크 오류에보다 탄력적입니다 (IMO). – dthorpe

+0

그리고 네, 루프의 또 다른 스레드가 있어야하고 어레이의 다음 데이터 청크가 사용 가능해질 때까지 기다렸다가 보내면 언제든지 보낼 수 있습니다. 내 말기의 "소비자 스레드"라고 설명합니다. 게시하다. 메인 UI 스레드가 이벤트 대기를 차단하는 것을 원하지 않습니다. UI가 멈추게 할 것이기 때문입니다. – dthorpe

0

동기화 된 멀티 스레드 다운로더를 만들려면 올바른 데이터 구조를 만들어야하며 byte[] 개 이상의 데이터가 필요합니다.

단계 :

    는 각 스레드에 의해 다운로드 5백킬로바이트에 대한 내용이나 고정 된 크기의 컨텐츠 다운 로더의 크기에 따라 여러 덩어리로 다운로드 브레이크
  1. .
  2. 스레드를 시작할 때 청크 색인 - 첫 번째 부분, 두 번째 부분 등을 지정하십시오.
  3. 다운로드가 가능할 때 청크 색인에 따라 최종 내용을 정렬하십시오.

관심이 있으시면 prozilla (C, Linux 기반 - at) 또는 Axel 코드를 살펴볼 수 있습니다.

+0

이미 해 보았습니다. 5 개의 덩어리 (스레드), 1MB가 있습니다. 그러나 전체 5 개의 스레드가 완료된 경우에만 데이터를 출력합니다. 단단한 순서를 어떻게 확인해야할지 모르기 때문입니다. – blez

+0

5 개의 스레드가 모두 완료 될 때까지 기다릴 필요가 없습니다. 또 다른 전략이 있습니다. 5MB 크기의 파일을 만들고 스레드 응답을 사용할 수있을 때 파일을 파일로 플러시하십시오! :) –

+0

예,하지만 데이터를 스트리밍해야하며 특히 파일을 사용하지 않아야합니다. 그게 내가 왜이 방법을 사용하지 못하게하는지. – blez

0

다운로드를 수행하는 코드와 여러 비동기 스레드를 시작하는 코드를 표시 할 수 있습니까?

아마도 시나리오를 완전히 이해하지 못했지만 내가 너라면 비동기 (responseStream에서 BeginRead)를 사용합니다. 그럼 난 할 것이다 다음 ....

void StartReading(Stream responseStream) 
{ 
    byte [] buffer = new byte[1024]; 
    Context ctx = new Context(); 
    ctx.Buffer = buffer; 
    ctx.InputStream = responseStream; 
    ctx.OutputStream = new MemoryStream(); // change this to be your output stream 

    responseStream.BeginRead(buffer, 0, buffer.Length; new AsyncCallback(ReadCallback), ctx); 
} 

void ReadCallback(IAsyncResult ar) 
{ 
    Context ctx = (Context)ar.AsyncState; 
    int read = 0; 
    try { 
     read = ctx.InputStream.EndRead(ar); 
     if (read > 0) 
     { 
      ctx.OutputStream.Write(ctx.Buffer, 0, read); 
      // kick off another async read 
      ctx.InputStream.BeginRead(ctx.Buffer, 0, ctx.Buffer.Length, new AsyncCallback(ReadCallback), ctx); 
     } else { 
      ctx.InputStream.Close(); 
      ctx.OutputStream.Close(); 
     } 
    } catch { 
    } 
} 

}