2014-09-17 3 views
0

한 번에 여러 스레드를 실행하고 싶습니다 (예 : 최대 5 개의 스레드). 둘 중 하나가 완료되면 새 스레드가 다른 데이터로 시작됩니다. (하나의 완료, 하나의 새로운 시작, 두 개의 완료, 두 개의 새로운 시작 ...)
Main for 루프는 기본 폼에 있지만 UI를 차단하지 않는 다른 스레드에서 실행됩니다.세마포어가 여러 스레드에서 작동하지 않는 것 같습니다.

실행하면 시각적으로 5 개의 웹 브라우저 컨트롤이 추가되고 페이지로드가 완료되면로드 된 컨트롤이 제거됩니다.
문제가 더 이상 폼에 추가되지 않습니다.
새 기능이 시작될 수 있도록 세마포가 제대로 릴리스되지 않았거나 다른 기능이 누락 되었습니까?

그리고 프로그램을 종료해도 종료되지 않고 계속 수행해야 할 작업이 많기 때문에 WaitHandle.WaitOne에서 차단됩니다.

더 많은 코드 명확성을 위해 불필요한 데이터를 일부 제거했습니다.

Semaphore pool = new Semaphore(5, 5); 
Scraper[] scraper = new Scraper[5]; 
Gecko.GeckoWebBrowser wb = null; 
int j = 0; 
for (int i = 0; i < arrScrapeboxItems.Count; i++) 
{ 
    pool.WaitOne(); 
    bool pustiMe = true; 
    while (pustiMe) 
    { 
     if (scraper[j] == null) scraper[j] = new Scraper(); 
     if (scraper[j].tred == null) 
     { 
      ScrapeBoxItems sbi = (ScrapeBoxItems)arrScrapeboxItems[i]; 

      doneEvents.Add(new ManualResetEvent(false)); // this is for WaitHandle.WaitAll after the for loop is done all the items 

      wb = new Gecko.GeckoWebBrowser(); 
      PoolObjects po = new PoolObjects(); 
      po.link = sbi.link; 
      // etc... 

      scraper[j].ThreadsCompleted += new Scraper.ThreadsHandler(frmMain_NextThreadItemsCompleted); 
      scraper[j].tred = new Thread(new ParameterizedThreadStart(scraper[j].Scrape)); 
      scraper[j].tred.Start(po); 

      pustiMe = false; 
      if (j == maxThreads - 1) 
       j = 0; 
      else 
       j++; 
      break; 
     } 
     else if (scraper[j].tred.IsAlive) // if the thread is finished, make room for new thread 
     { 
      scraper[j] = null; 
     } 
     if (pustiMe) Thread.Sleep(1000); 
    } 
} 

// event from Scraper class 
void frmMain_ThreadsCompleted() 
{ 
    pool.Release(); 
} 

그리고 스크레이퍼 클래스 모양 같은

:

public void Scrape(object o) 
{ 
    po = (PoolObjects)o; 
    // do stuff with po 

    po.form.Invoke((MethodInvoker)delegate 
    { 
     po.form.Controls.Add(po.wb); 
     po.wb.DocumentCompleted += new EventHandler<Gecko.Events.GeckoDocumentCompletedEventArgs>(wb_DocumentCompleted); 
     po.wb.Navigate(po.link); 
    }); 
} 

void wb_DocumentCompleted(object sender, Gecko.Events.GeckoDocumentCompletedEventArgs e) 
{ 
    var br = sender as Gecko.GeckoWebBrowser; 
    if (br.Url == e.Uri) 
    { 
     form.Controls.Remove(po.wb); 
     ThreadsCompleted(); 
     manualReset.Set(); 
    } 
} 
+0

2 가지 가능한 문제처럼 보입니다. 새 스레드를 만들고 폼에 추가하는 대신 모든 스레드에 동일한 웹 브라우저를 사용하고 있습니다. 둘째로, 폼에서 웹 브라우저의 좌표와 크기를 지정하지 않습니다. 그들은 아마도 서로의 위에 있습니다. – TyCobb

+0

@TyCobb 브라우저 객체를 for 루프 안으로 옮겼습니다.하지만 아무런 문제없이 같은 문제가있었습니다. 나는 브라우저 컨트롤을 수평으로 배치하기위한 코드를 가지고 있으므로 서로 위에 있지 않습니다. 뭔가 다른 문제가 있습니다. 답변 감사합니다. – lopkiju

답변

2

어느 당신은 오타 나 큰 버그가 있습니다. 당신은 당신이 if (!scraper[j].tred.IsAlive)을 원하는 내가

else if (scraper[j].tred.IsAlive) 
{ 
    scraper[j] = null; 
} 

생각 있습니다. 그렇지 않으면 배열에 활성 Scraper 참조를 덮어 쓰게됩니다.

더 많은 것을 말하자면, Scraper 개체 배열을 유지하려고하면 실제로 필요하지 않은 복잡한 문제가 발생합니다. 이미 얼마나 많은 동시 스레드를 제어 할 수있는 세마포어가 있으므로 Scraper 배열은 불필요한 노이즈입니다.

또한 ManualResetEvent 개체를 기다리는 것을 원하지 않습니다. WaitAll은 63 개가 넘는 항목을 기다릴 수 없으므로 항목 목록에있는 항목보다 많은 항목이있는 경우 WaitAll은 처리하지 않습니다. 모든 작업이 완료되었는지보다 나은 방법을 보여줍니다.

for (int i = 0; i < arrScrapeboxItems.Count; i++) 
{ 
    pool.WaitOne(); 
    ScrapeBoxItems sbi = (ScrapeBoxItems)arrScrapeboxItems[i]; 

    wb = new Gecko.GeckoWebBrowser(); 
    PoolObjects po = new PoolObjects(); 
    po.link = sbi.link; 
    // more initialization of po ... 

    // and then start the thread 
    Thread t = new Thread(ScrapeThreadProc); 
    t.Start(po); 
} 
// Here's how you wait for all of the threads to complete. 
// You have your main thread (which is running here) call `WaitOne` on the semaphore 5 times: 
for (int i = 0; i < 5; ++i) 
{ 
    pool.WaitOne(); 
} 

private void ScrapeThreadProc(object o) 
{ 
    var po = (PoolObjects)o; 
    Scraper scraper = new Scraper(); 
    // initialize your Scraper object 
    scraper.ThreadsCompleted += new Scraper.ThreadsHandler(frmMain_NextThreadItemsCompleted); 

    scraper.Scrape(po); 

    // scraping is done. Dispose of the scraper and the po. 

    // and then release the semaphore 
    pool.Release(); 
} 

이렇게하면 코드가 크게 단순 해집니다.

주 스레드가 세마포어에서 5 번 대기하는 것의 아이디어는 매우 간단합니다. 주 스레드가 Release을 호출하지 않고 5 번 세마포어를 획득 할 수 있으면 다른 작업이 실행 중이 아님을 알 수 있습니다.

이렇게하는 다른 방법도 있지만, 코드를 좀 더 복잡하게 재구성해야합니다. 작업 병렬 라이브러리, 특히 Parallel.ForEach을 살펴 봐야합니다. 그러면 스레드 스레딩이 처리됩니다. 동시 스레드의 최대 수를 5로 설정하여 한 번에 너무 많은 스레드를 가지지 않도록 할 수 있습니다.

BlockingCollection 또는 다른 공유 대기열을 사용하는 생산자/사용자 설정을 사용하여이 작업을 수행 할 수도 있습니다.

두 시나리오 모두에서 목록의 항목을 공동으로 처리하는 5 개의 영구 스레드가 생성됩니다. 이는 일반적으로 각 항목에 대해 하나의 스레드를 만드는 것보다 효율적입니다.

+0

의견에서 내가 뭘 생각했는지 썼지 만 if (scraper [j] .tred.IsAlive) 코드에 썼습니다. 당신이 옳았. 그리고 이것이 부족한! ' 문제 였어, 고마워! WaitAll을 쓰레드를 기다리는 방법으로 대체하고 ManualResetEvent를 제거했습니다. 다시 감사합니다! – lopkiju

관련 문제