Несколько потоков - PullRequest
       31

Несколько потоков

4 голосов
/ 08 февраля 2010

Я много пишу здесь о многопоточности, и большое сообщество stackoverflow помогло мне много в понимании многопоточности.

Все примеры, которые я видел в Интернете, имеют дело только с одной веткой.

Мое приложение - скребок для страховой компании (семейная компания ... все бесплатно). В любом случае, пользователь может выбрать, сколько потоков он хочет запустить. Допустим, например, что пользователь хочет, чтобы приложение очистило 5 сайтов одновременно, а затем в тот же день он выбирает 20 потоков, потому что его компьютер больше ничего не делает, поэтому у него есть запасные ресурсы.

По сути, приложение создает список, скажем, 1000 сайтов для чистки. Поток отключается, делает это, обновляет пользовательский интерфейс и создает список.

Когда это закончено, вызывается другая нить, чтобы начать очистку. В зависимости от количества потоков, которые пользователь установил для использования, он создаст количество потоков x.

Какой лучший способ создать эти темы? Должен ли я создать 1000 потоков в списке. И перебрать их? Если пользователь настроил запуск 5 потоков, он будет циклически проходить по 5 за раз.

Я понимаю многопоточность, но логика приложения ловит меня.

Какие-нибудь идеи или ресурсы в сети, которые могут мне помочь?

Ответы [ 9 ]

3 голосов
/ 08 февраля 2010

Вы можете использовать пул потоков для этого:

using System;
using System.Threading;

public class Example
{
    public static void Main()
    {
        ThreadPool.SetMaxThreads(100, 10);

        // Queue the task.
        ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));

        Console.WriteLine("Main thread does some work, then sleeps.");

        Thread.Sleep(1000);

        Console.WriteLine("Main thread exits.");
    }

    // This thread procedure performs the task.
    static void ThreadProc(Object stateInfo)
    {
        Console.WriteLine("Hello from the thread pool.");
    }
}
2 голосов
/ 08 февраля 2010

Этот скребок, много ли он использует ЦП при работе?

Если он много общается с этими 1000 удаленными сайтами, загружая их страницы, это может занять больше времени, чем фактический анализ страниц.

А сколько процессорных ядер у вашего пользователя? Если у них есть 2 (что является обычным в наши дни), то после двух одновременных потоков, выполняющих анализ, они не увидят никакого ускорения.

Так что вам, вероятно, нужно «распараллелить» загрузку страниц. Я сомневаюсь, что вам нужно сделать то же самое для анализа страниц.

Взгляните на асинхронный ввод-вывод вместо явной многопоточности. Это позволяет запускать несколько загрузок параллельно, а затем перезванивать после завершения каждой из них.

1 голос
/ 08 февраля 2010

Если вы действительно хотите приложение, используйте что-то, что кто-то уже потратил на разработку и совершенствование:

http://arachnode.net/

arachnode.net - это полный и всесторонний сканер .NET для загрузка, индексация и хранение Интернет-контент, включая электронную почту адреса, файлы, гиперссылки, изображения, и веб-страницы.

Будь заинтересован или вовлечен в очистка экрана, интеллектуальный анализ данных, текст майнинг, исследования или любые другие приложение, где высокая производительность приложение для сканирования является ключом к успех ваших усилий, arachnode.net предоставляет решение тебе нужно для успеха.

Если вы также хотите написать его самостоятельно, потому что писать это весело (я написал один не так давно, и да, это очень весело), ​​тогда вы можете обратиться к этому pdf, предоставленному arachnode.net, который действительно объясняет подробно теория хорошего веб-сканера:

http://arachnode.net/media/Default.aspx?Sort=Downloads&PageIndex=1

Загрузите PDF-файл, озаглавленный «Crawling the Web» (вторая ссылка сверху). Перейдите к разделу 2.6, озаглавленному: «2.6 Многопоточные сканеры». Это то, что я использовал для сборки своего сканера, и я должен сказать, я думаю, что он работает довольно хорошо.

1 голос
/ 08 февраля 2010

Я думаю, что этот пример в основном то, что вам нужно.

public class WebScraper
{
    private readonly int totalThreads;
    private readonly List<System.Threading.Thread> threads;
    private readonly List<Exception> exceptions;
    private readonly object locker = new object();
    private volatile bool stop;

    public WebScraper(int totalThreads)
    {
        this.totalThreads = totalThreads;
        threads = new List<System.Threading.Thread>(totalThreads);
        exceptions = new List<Exception>();

        for (int i = 0; i < totalThreads; i++)
        {
            var thread = new System.Threading.Thread(Execute);
            thread.IsBackground = true; 
            threads.Add(thread);
        }
    }

    public void Start()
    {
        foreach (var thread in threads)
        {
            thread.Start();
        }
    }

    public void Stop()
    {
        stop = true;
        foreach (var thread in threads)
        {
            if (thread.IsAlive)
            {
                thread.Join();                      
            }
        }
    }

    private void Execute()
    {
        try
        {
            while (!stop)
            {
                // Scrap away!                      
            }
        }
        catch (Exception ex)
        {
            lock (locker)
            {
                // You could have a thread checking this collection and
                // reporting it as you see fit.
                exceptions.Add(ex);
            }
        }
    }
}
0 голосов
/ 08 февраля 2010

Я бы использовал очередь, условную переменную и мьютекс и запустил только запрошенное количество потоков, например, 5 или 20 (а не начало 1000).

Каждый поток блокирует переменную условия. Проснувшись, он удаляет первый элемент, разблокирует очередь, работает с элементом, блокирует очередь и проверяет наличие дополнительных элементов. Если очередь пуста, спите по условной переменной. Если нет, разблокируйте, работайте, повторите.

Пока мьютекс заблокирован, он также может проверить, не запросил ли пользователь количество потоков, которое должно быть уменьшено. Просто проверьте, если число> max_count, и если так, поток завершает себя.

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

Каждый раз, когда пользователь увеличивает запрошенное число потоков, просто запустите их, и они заблокируют очередь, проверит работу и либо перейдут в режим ожидания переменной условия, либо начнут работать.

Каждый поток будет постоянно извлекать больше работы из очереди или спать. Вам не нужно больше 5 или 20.

0 голосов
/ 08 февраля 2010

Рассмотрите возможность использования асинхронного шаблона на основе событий (классы AsyncOperation и AsyncOperationManager)

0 голосов
/ 08 февраля 2010

Я решил аналогичную проблему, создав рабочий класс, который использует обратный вызов, чтобы сообщить основному приложению, что рабочий уже сделан. Затем я создаю очередь из 1000 потоков, а затем вызываю метод, который запускает потоки до тех пор, пока не будет достигнут предел количества запущенных потоков, отслеживая активные потоки с помощью словаря с ключом ManagedThreadId потока. Когда каждый поток завершается, обратный вызов удаляет свой поток из словаря и вызывает средство запуска потока.

Если соединение разорвано или время ожидания истекло, обратный вызов повторно вставляет поток обратно в очередь. Зафиксируйте очередь и словарь. Я создаю потоки против использования пула потоков, потому что накладные расходы на создание потока незначительны по сравнению со временем соединения, и это позволяет мне иметь намного больше потоков в полете. Обратный вызов также предоставляет удобное место для обновления пользовательского интерфейса, даже позволяя вам изменить ограничение потока во время его работы. У меня было более 50 открытых соединений одновременно. Не забудьте увеличить свойство MacConnections в файле app.config (по умолчанию два).

0 голосов
/ 08 февраля 2010

Основная логика:

У вас есть одна очередь, в которую вы помещаете URL-адреса для очистки, затем вы создаете свои потоки и используете объект очереди, к которому имеет доступ каждый поток. Пусть потоки начнут цикл:

  1. заблокировать очередь
  2. проверить, есть ли элементы в очереди, если нет, разблокировать очередь и завершить поток
  3. снять первый элемент в очереди
  4. очередь разблокировки
  5. позиция процесса
  6. вызвать событие, которое обновляет интерфейс (не забудьте заблокировать контроллер интерфейса)
  7. возврат к шагу 1

Просто позвольте потокам выполнять часть «получить материал из очереди» (извлекать задания) вместо того, чтобы давать им URL-адреса (подталкивать задания), так что вы просто скажете

* * 1 022 YourThreadManager.StartThreads (numberOfThreadsTheUserWants); * * тысячу двадцать-три

и все остальное происходит автоматически. Посмотрите другие ответы, чтобы узнать, как создавать и управлять потоками.

0 голосов
/ 08 февраля 2010

Возможно, вы захотите взглянуть на статью ProcessQueue о CodeProject.

По сути, вам нужно создать (и запустить) количество подходящих потоков, в вашем случае это число исходит от пользователя. Каждый из этих потоков должен обработать сайт, а затем найти следующий сайт, необходимый для обработки. Даже если вы не используете сам объект (хотя он звучит так, как будто он вполне соответствует вашим целям, хотя я явно предвзят!), Он должен дать вам хорошее представление о как такого рода вещи будет сделано.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...