Отдельные потоки для ввода и вывода сокета - PullRequest
6 голосов
/ 17 ноября 2009

Мне поручили работать над некоторыми проблемами производительности и случайных сбоев многопоточного Java-сервера. Хотя потоки и безопасность потоков не являются для меня действительно новыми темами, я обнаружил, что разработка нового многопоточного приложения, вероятно, вдвое сложнее, чем попытка настроить какой-то унаследованный код. Я пролистал несколько известных книг в поисках ответов, но странно то, что, пока я читаю об этом и анализирую предоставленные примеры, все кажется ясным. Однако, как только я посмотрю код, над которым я должен работать, я больше не уверен ни в чем! Должно быть, слишком много теоретических знаний и мало реального опыта или что-то в этом роде.

В любом случае, возвращаясь к теме, поскольку я проводил некоторые онлайн-исследования, я наткнулся на этот фрагмент кода . Вопрос, который продолжает беспокоить меня: Действительно ли безопасно вызывать getInputStream () и getOutputStream () на сокете из двух отдельных потоков без синхронизации? Или я сейчас слишком параноидален по поводу всего потока вопрос безопасности? Думаю, это то, что происходит, когда, как в 5-й книге подряд, рассказывается, сколько вещей может пойти не так с параллелизмом.

PS. Извините, если вопрос слишком длинный или, может быть, слишком «нубийский», пожалуйста, будьте осторожны со мной - это мой первый пост здесь.

Редактировать: Просто для ясности, я знаю, что сокеты работают в полнодуплексном режиме, и безопасно одновременно использовать их входные и выходные потоки. Мне хорошо, когда вы получаете эти ссылки в главном потоке, а затем инициализируете объекты потока с ними, но безопасно ли также получать эти потоки в двух разных потоках?

@ rsp:

Итак, я проверил код Sun и PlainSocketImpl синхронизирует эти два метода, как вы сказали. Socket, однако, нет. getInputStream() и getOutputStream() в значительной степени просто оболочки для SocketImpl, поэтому , вероятно, проблемы параллелизма не приведут к взрыву всего сервера. Тем не менее, с небольшой неудачей синхронизации кажется, что вещи могут пойти не так (например, когда какой-то другой поток закрывает сокет, когда метод уже проверен на наличие ошибок).

Как вы указали, с точки зрения структуры кода было бы неплохо предоставить каждому потоку ссылку на поток вместо целого сокета. Я бы, вероятно, уже реструктурировал код, над которым я работаю, если бы не тот факт, что каждый поток также использует метод сокета close() (например, когда сокет получает команду «shutdown»). Насколько я могу судить, основная цель этих потоков - поставить в очередь сообщения для отправки или обработки, поэтому, возможно, это нарушение принципа единой ответственности, и эти потоки не должны закрывать сокет (сравните с Separated Модемный интерфейс )? Но потом, если я продолжаю анализировать код слишком долго, кажется, что дизайн, как правило, несовершенен, и все это требует переписывания. Даже если бы руководство было готово заплатить цену, серьезный рефакторинг унаследованного кода, отсутствие модульных тестов и решение сложных проблем параллелизма скорее всего принесет больше вреда, чем пользы. Не так ли?

1 Ответ

3 голосов
/ 17 ноября 2009

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

Редактировать : классы реализации сокетов от Sun синхронизируют методы getInputStream() и getOutputStream(), вызывая их из разных потоков; Однако я согласен с вами, что передача потоков в потоки, использующие их, может иметь больше смысла с точки зрения структуры кода (например, внедрение зависимостей помогает при тестировании.)

...