2009-12-08 1 views
0

Я использую класс TcpClient в C#.C#: Обработка сигнала завершения в потоке обработчика TCP?

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

Мое решение для каждого из этих обработчиков нити следующим образом:

1 Check NetworkStream's DataAvailable method 
    1.1 If new data available then read and process new data 
    1.2 If end of stream then self terminate 
2 Check for terminate signal from main thread 
    2.1 If terminate signal activated then self terminate 
3 Goto 1. 

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

Есть ли лучший способ сделать это?

+0

Почему существует wiki сообщества? –

+0

Я случайно нажал на эту опцию и не могу ее изменить !!! – Lopper

ответ

2

См. Asynchronous Server Socket Example, чтобы узнать, как это сделать «.NET way», без создания новых потоков для каждого запроса.

+0

Однако способ асинхронного ввода-вывода не предусматривает способ предоставления прерывания. Скажем, почему-то пользователь хочет завершить работу приложения, и есть какой-то асинхронный вызов, который еще не вызвал соответствующую функцию обратного вызова, потому что никаких входящих данных еще не произошло, но немедленное завершение приложения должно быть незамедлительно. – Lopper

+0

Ну, Async IO будет использовать потоки (пул) внутри, а не создавать их. Поэтому, на мой взгляд, если у вас есть большое количество долгоживущих соединений, лучше иметь выделенный поток для каждого из них, а не использовать пул потоков, поскольку это может повлиять на другие приложения, которые используют ThreadPool. – A9S6

+1

@ Lopper: один из способов прекращения - просто не начать следующую операцию. Готово чтение? Не делайте другого BeginRead. Я также подозреваю, что закрытие экземпляра 'TcpClient' приведет к отмене. –

0

Помню, что я работаю над аналогичным сервисом Windows. Это сервер NTRIP, который может принимать около 1000 TCP-соединений и направлять данные в NTRIP Caster.

Если у вас есть выделенный сервер для этого приложения, это не будет проблемой, если вы не добавите больше кода для каждого потока (File IO, Database и т. Д.), Хотя в моем случае у меня также была обработка базы данных для входа в систему/выхода для каждого соединения).

Вещь, чтобы не упустить:

  1. Bandwidth, когда нить доходит до 600 или около того. Вы начинаете видеть отсоединения когда окно TCP буфера забиты по какой-то причине или доступная полоса пропускание дотягивает
  2. Операционная система, на которой вы работаете это приложение может иметь некоторые ограничения, которые могут привести к Отключению

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

0

Вы правы, что не хотите, чтобы все ваши потоки «заняты ожиданием» (т. Е. Выполняли небольшой цикл снова и снова). Вы либо хотите, чтобы они блокировались, либо вы хотите использовать асинхронный ввод-вывод.

Как отметил Джон Сондерс, асинхронный ввод-вывод - это «правильный путь», поскольку он может масштабироваться до сотен подключений. В принципе, вы вызываете BeginRead() и передаете ему функцию обратного вызова. BeginRead() немедленно возвращается, и когда данные поступают, функция обратного вызова вызывается в потоке из пула потоков. Функция обратного вызова обрабатывает данные, снова вызывает BeginRead(), а затем возвращает, которая возвращает поток обратно в пул.

Однако, если вы будете открывать только несколько подключений одновременно, совершенно нормально создавать поток для каждого соединения. Вместо проверки свойства DataAvailable в цикле продолжите вызов и вызовите функцию Read(). Поток блокирует, не потребляя процессор, пока данные не будут доступны для чтения. Если соединение потеряно или вы закрываете его из другого потока, вызов Read() выдает исключение, которое вы можете обработать, прекратив поток вашего читателя.

+0

Однако способ асинхронного ввода-вывода не обеспечивает способ предоставления терминации. Скажем, по какой-то причине пользователь хочет завершить работу приложения, и есть асинхронный вызов, который еще не вызвал соответствующую функцию обратного вызова, потому что никаких входящих данных еще не произошло. – Lopper

1

Верьте или нет, что 1000 тиков сна действительно будут держать все в движении.

private readonly Queue<Socket> sockets = new Queue<Socket>(); 
private readonly object locker = new object(); 
private readonly TimeSpan sleepTimeSpan = new TimeSpan(1000); 
private volatile Boolean terminate; 

private void HandleRequests() 
{ 
    Socket socket = null; 

    while (!terminate) 
    { 
     lock (locker) 
     { 
      socket = null; 
      if (sockets.Count > 0) 
      { 
       socket = sockets.Dequeue(); 
      } 
     } 

     if (socket != null) 
     { 
      // process 
     } 

     Thread.Sleep(sleepTimeSpan); 
    } 
} 
+0

Исправьте меня, если я ошибаюсь, но что приведенный выше код означает, что только один поток будет выполнять чтение/обработку входящих данных из tcp-соединения. Если для обработки данных внутри блока if (socket! = Null) требуется значительное время, я предполагаю, что перед обработкой другого соединения будет значительная задержка. Есть ли лучший способ, когда обработка каждого сокета может происходить параллельно, но в то же время не использовать метод опроса? – Lopper

+0

Нет, это метод, который вы передаете потокам обработчиков при их создании. – ChaosPandion

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