CAsyncSocket и многопоточность

Я использую MFC CAsyncSocket для сетевого взаимодействия в многопоточной среде. После нескольких часов попыток заставить принятые сокеты принимать входящие данные я наткнулся на страницу, в которой говорится, что для вызова функции OnReceive CAsyncSocket сокет должен находиться в контексте основного потока графического интерфейса. Перемещение его в основной поток GUI позволило сокету начать получать данные.

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


person Noaki    schedule 06.04.2009    source источник


Ответы (1)


Было бы проще просто создать отдельный поток с собственной очередью сообщений для ваших сокетов? Я не думаю, что CAsyncSocket нужно создавать в основной очереди сообщений, просто в какой-то очереди сообщений. См. документацию по CWinThread, чтобы узнать, как создать отдельный поток с собственной очередью сообщений, совместимой с MFC.

К сожалению, крайне важно, чтобы вы вызывали все операции с сокетами из контекста нового потока. MFC использует глобальное состояние в скрытых классах, которые используют локальное хранилище потока для хранения информации о потоке, и эта информация используется во многих методах CAsynchSocket. Это означает, что CAsynchSocket имеет сходство с потоком, и вы всегда должны использовать и создавать его в любом потоке, который должен быть его насосом сообщений.

Одним из подходов может быть создание CWinThread, создание собственного пользовательского скрытого окна MFC в этом потоке (путем создания окна в контексте этого потока) и создание сообщений и обработчиков сообщений в этом окне для всех операций сокета (создание, подключение и т. д.) вы делаете. Убедитесь, что поток перекачивает сообщения (это делает метод "Run()"), а затем публикуйте/отправляйте сообщения в ваше окно для управления вашими сокетами.

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

Если вы беспокоитесь о влиянии на дизайн, просто создайте собственный прокси-объект CThreadSafeAsynchSocket и делегируйте его реальной реализации посредством передачи сообщений скрытому окну. Вы можете использовать SendMessage для блокировки операций и PostMessage для асинхронных операций. Если вы оберните конструктор в фабричный объект, вы можете отложить создание потока сокета до тех пор, пока он не понадобится.

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

person David Gladfelter    schedule 06.04.2009
comment
В моем исходном коде использовался поток с насосом сообщений (протестирован и работает с отправкой через сокет), но все еще не позволял вызывать OnReceive. - person Noaki; 06.04.2009