Мое эмпирическое правило заключается в том, что любой данный сокет должен работать только с одним потоком (*). Таким образом, если вы создаете отдельный поток ввода-вывода для каждого сокета, и ваш основной поток хочет что-то записать в сокет потока ввода-вывода, тогда основной поток должен отправить эти данные в поток ввода-вывода, после чего поток ввода-вывода можно записать в сокет.
Конечно, это означает, что вам нужен хороший метод связи между основным потоком и потоком ввода-вывода; что вы можете сделать, создав пару сокетов для каждого потока ввода-вывода и имея потоки ввода-вывода select () / poll () на своем конце пары сокетов (для обработки данных, поступающих из основного потока). как на их сетевом сокете.
Но как только вы это сделаете, вы столкнетесь со сложностью использования select () / poll () И многопоточности, что приводит к большим накладным расходам. Поэтому, если по какой-то причине вам абсолютно не нужна многопоточность, я согласен с предыдущими постерами - лучше всего обрабатывать все сокеты в одном потоке с помощью select () или poll ().
(*) Возможно одновременное чтение / запись в один и тот же сокет нескольких потоков, но он подвержен ошибкам. В частности, последовательности запуска и выключения могут быть сложными, чтобы получить 100% правильно. Вот почему я стараюсь не делить данный сокет между несколькими потоками.