Тип потока для цикла TCP Accept: BackgroundWorker, Thread или ThreadPool - PullRequest
0 голосов
/ 01 октября 2010

Я пишу TCP-сервер, и в его основе лежит довольно стандартный фрагмент кода bind-listen-accept, красиво инкапсулированный TcpListener.Код, который я сейчас запускаю в разработке , работает , но я ищу обсуждение выбранной модели потока:

        // Set up the socket listener
        // *THIS* is running on a System.Threading.Thread, of course.
        tpcListener = new TcpListener(IPAddress.Any, myPort);
        tpcListener.Start();
        while (true)
        {
            Socket so = tpcListener.AcceptSocket();
            try
            {
                MyWorkUnit work = new MyWorkUnit(so);
                BackgroundWorker bw = new BackgroundWorker();
                bw.DoWork += new DoWorkEventHandler(DispatchWork);
                bw.RunWorkerCompleted += 
                                new RunWorkerCompletedEventHandler(SendReply);
                bw.RunWorkerAsync(work);
            }
            catch (System.Exception ex)
            {
                EventLogging.WindowsLog("Error caught: " + 
                                     ex.Message, EventLogging.EventType.Error);
            }
        }

Я видел хорошие описания такого родавыбранной модели потока (BackgroundWorker, Stock Thread или ThreadPool), но ни одна из них для подобной ситуации.Хорошая сводка плюсов и минусов каждого из них: backgroundworker-vs-background-thread (второй ответ).В приведенном выше примере кода я выбрал BackgroundWorker, потому что это было легко.Пришло время выяснить, является ли это правильным способом сделать это.

Это особенности этого приложения, и они, вероятно, довольно стандартны для большинства транзакционных TCP-серверов:

  • Не приложение Windows Forms.На самом деле, он запускается как служба Windows.
  • (я не уверен, должна ли порожденная работа быть потоком переднего плана или нет. Сейчас я запускаю их в качестве фона, и все в порядке.)
  • Независимо от того, какой приоритет назначен потоку, он подходит, если цикл Accept() получает циклы.
  • Мне не нужен фиксированный идентификатор для потоков на более поздний период Abort() или любой другой.
  • Задачи, выполняемые в потоках, короткие - не более секунд.
  • Потенциально множество задач может очень быстро попасть в этот цикл.
  • «Изящный» способ отказаться (или поставить в очередь) новая работа была бы хороша, если бы у меня не было ниток.

Так какой же правильный путь для этого?

Ответы [ 3 ]

3 голосов
/ 01 октября 2010

Для этого основного потока используйте отдельный объект Thread.Это долго работает, что менее подходит для ThreadPool (и Bgw использует ThreadPool).

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

Редактировать

А для входящих запросов вы можете использовать ThreadPool (напрямую или через Bgw), но учтите, что это может повлиять на вашу пропускную способность.Когда все потоки заняты, перед созданием дополнительного потока возникает задержка (0,5 с).Такое поведение ThreadPool может быть полезным или нет.Вы можете настроить MinThreads для некоторого управления им.

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

Все зависит от того, сколько запросов вы ожидаете, и насколько они велики.

1 голос
/ 01 октября 2010

BackgroundWorker, кажется, является достойным выбором для ваших работников.Мое единственное предостережение - убедиться, что вы не блокируете сетевой трафик на самих потоках.При необходимости используйте асинхронные методы для отправки / получения туда, чтобы потоки ThreadPool не блокировались для сетевого трафика.

Хорошо (действительно, действительно), что эти потоки также являются фоновыми потоками.Единственное реальное отличие состоит в том, что в нормальных условиях поток Foreground будет поддерживать процесс живым.

Также я не думаю, что вы упомянули этот основной поток, который захватывает эти соединения;этот подходит для обычного экземпляра System.Threading.Thread.

0 голосов
/ 01 октября 2010

Ну, лично ни один из них не будет моим первым выбором.Я склонен отдавать предпочтение асинхронным операциям ввода-вывода, используя преимущества Socket.BeginReceive и Socket.BeginSend и позволяя базовым портам завершения ввода-вывода выполнять всю работу за вас.Но если вы предпочитаете использовать синхронные операции ввода-вывода, тогда следующим лучшим вариантом будет переключение их на ThreadPool или Task (при использовании .NET 4.0).

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