2014-09-17 3 views
0

Я хочу запускать несколько потоков одновременно (максимум 5 потоков, например), и когда один из них заканчивается, новый начинается с разных данных. (один заканчивается, один новый старт, два окончания, два новых начала ...)
Основной для цикла в основной форме, но выполняется из другого потока, чтобы не блокировать пользовательский интерфейс.Семафор, похоже, не работает с несколькими потоками

Когда я запускаю его, программа добавляет 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

Похоже на две возможные проблемы. Вы используете один и тот же веб-браузер для каждого потока вместо создания нового и добавляете его в форму. Во-вторых, вы не указываете координаты и размер веб-браузера в форме. Они, вероятно, находятся на вершине друг друга. – TyCobb

+0

@TyCobb Я переместил объект браузера внутри цикла, но безрезультатно, та же проблема. У меня есть код для управления браузером по горизонтали, поэтому он не на вершине друг друга. Здесь есть что-то еще. Спасибо за ответ. – lopkiju

ответ

2

Либо у вас есть опечатка или огромная ошибка. У вас есть

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

Я думаю вы хотите if (!scraper[j].tred.IsAlive). В противном случае вы закончите перезаписывать активную ссылку 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 раз довольно проста. Если основной поток может получить семафор 5 раз без вызова Release, то вы знаете, что никаких других заданий не выполняется.

Есть и другие способы сделать это, но они потребуют еще более сложной реструктуризации вашего кода. Вы должны заглянуть в параллельную библиотеку задач, в частности Parallel.ForEach, которая будет обрабатывать потоки для вас. Вы можете установить максимальное количество одновременных потоков на 5, так что вы не получите слишком много потоков одновременно.

Вы также можете сделать это, используя настройку производителя/потребителя с помощью BlockingCollection или другой общей очереди.

В обоих этих сценариях вы создаете 5 постоянных потоков, которые совместно обрабатывают элементы из списка. Это обычно более эффективно, чем создание одного потока для каждого элемента.

+0

В комментариях написано то, о чем я думал, но написал if (scraper [j] .tred.IsAlive) в коде. Ты был прав. И этого не хватает! была проблема, спасибо! Я заменил WaitAll вашим способом ожидания потоков и удалил ManualResetEvent. Еще раз спасибо! – lopkiju

Смежные вопросы