Лучшая практика многопоточного дизайна - PullRequest
3 голосов
/ 13 августа 2008

Рассмотрим эту проблему: у меня есть программа, которая должна получить (скажем) 100 записей из базы данных, а затем для каждой из них она должна получать обновленную информацию из веб-службы. Есть два способа ввести параллелизм в этом сценарии:

  1. Я запускаю каждый запрос к веб-сервису в новой теме. Количество одновременных потоков контролируется некоторым внешним параметром (или динамически настраивается каким-либо образом).

  2. Я создаю меньшие партии (скажем, по 10 записей в каждой) и запускаю каждую партию в отдельном потоке (в нашем примере 10 потоков).

Какой подход лучше и почему вы так думаете?

Ответы [ 4 ]

6 голосов
/ 14 августа 2008

Вариант 3 лучший:

Использовать Async IO.

Если ваша обработка запросов не является сложной и тяжелой, ваша программа будет тратить 99% своего времени на ожидание HTTP-запросов.

Это именно то, для чего предназначен Async IO - пусть сетевой стек Windows (или .net framework или что-то еще) беспокоится обо всех ожиданиях и просто использует один поток для отправки и «получения» результатов.

К сожалению, .NET Framework делает правильную боль в заднице. Это проще, если вы просто используете сырые сокеты или Win32 API. Вот (проверено!) Пример использования C # 3 в любом случае:

using System.Net; // need this somewhere

// need to declare an class so we can cast our state object back out
class RequestState {
    public WebRequest Request { get; set; }
}

static void Main( string[] args ) {
    // stupid cast neccessary to create the request
    HttpWebRequest request = WebRequest.Create( "http://www.stackoverflow.com" ) as HttpWebRequest;

    request.BeginGetResponse(
        /* callback to be invoked when finished */
        (asyncResult) => { 
            // fetch the request object out of the AsyncState
            var state = (RequestState)asyncResult.AsyncState; 
            var webResponse = state.Request.EndGetResponse( asyncResult ) as HttpWebResponse;

            // there we go;
            Debug.Assert( webResponse.StatusCode == HttpStatusCode.OK ); 

            Console.WriteLine( "Got Response from server:" + webResponse.Server );
        },
        /* pass the request through to our callback */
        new RequestState { Request = request }  
    );

    // blah
    Console.WriteLine( "Waiting for response. Press a key to quit" );
    Console.ReadKey();
}

EDIT:

В случае .NET «обратный вызов завершения» фактически запускается в потоке ThreadPool, а не в вашем основном потоке, поэтому вам все равно придется блокировать любые общие ресурсы, но это все же избавляет вас от необходимости управлять резьб.

2 голосов
/ 13 августа 2008

Две вещи для рассмотрения.

1. Сколько времени займет обработка записи?

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

Если обработка записей длится достаточно долго, разница будет незначительной, поэтому более простой подход (1 запись на поток), вероятно, лучший.

2. Сколько потоков вы планируете начать?

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

0 голосов
/ 13 августа 2008

Получите Параллельный Fx . Посмотрите на BlockingCollection. Используйте поток для подачи пакетов записей, а 1 - n потоков извлекают записи из коллекции для обслуживания. Вы можете контролировать скорость подачи коллекции и количество потоков, обращающихся к веб-сервисам. Сделайте его настраиваемым с помощью ConfigSection, и сделайте его универсальным, передавая делегатам Action коллекции, и у вас будет хороший маленький дозатор, который вы сможете использовать для своего сердца.

0 голосов
/ 13 августа 2008

Компьютер, на котором запущена программа, вероятно, не является узким местом, поэтому: Помните, что протокол HTTP имеет заголовок keep-alive, который позволяет отправлять несколько запросов GET на одни и те же сокеты, что избавляет вас от дрожания TCP / IP. К сожалению, я не знаю, как использовать это в библиотеках .net. (Должно быть возможно.)

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

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