Сетевая многопоточность

Я программирую онлайн-игру по двум причинам: первая для ознакомления с запросами сервера / клиента в среде реального времени (в отличие от чего-то вроде обычного веб-браузера, который работает не в реальном времени) и для того, чтобы на самом деле намочить руки в этой области , так что я могу приступить к собственно правильному проектированию.

Anywho, я делаю это на C++, и я использую winsock для обработки моих основных, базовых сетевых тестов. Я, очевидно, хочу использовать ограничитель кадров и в какой-то момент запустить 3D и все такое, и моя основная проблема в том, что когда я выполняю send () или receive (), программа любезно бездействует и ждет ответа. Это может привести к скорости 8 кадров в секунду даже при лучшем подключении к Интернету.

Поэтому для меня очевидное решение - вынуть сетевой код из основного процесса и запустить его в отдельном потоке. В идеале я бы назвал "send" в моем основном процессе, который передал бы сетевому потоку указатель на сообщение, а затем периодически (каждый кадр) проверял, получил ли сетевой поток ответ, или истекло ли время ожидания, или что-то еще. есть ты. В идеальном мире у меня на самом деле было бы 2 или более сетевых потока, работающих одновременно, так что я мог бы сказать, запустить окно чата и выполнить фоновую загрузку элемента брони, и при этом позволить игроку бегать сразу.

Основная моя проблема в том, что для меня это в новинку. Я понимаю концепция потоковой передачи, но вижу некоторые серьезные проблемы, например, что произойдет, если два потока попытаются прочитать / записать один и тот же адрес памяти одновременно и т. д. Я знаю, что уже существуют методы для обработки такого рода из вещей, поэтому я ищу предложения о том, как лучше всего реализовать что-то подобное. По сути, мне нужен поток A, чтобы иметь возможность запускать процесс в потоке B, отправив фрагмент данных, опросив статус потока B, а затем получив ответ, также как фрагмент данных, в идеале без каких-либо серьезных сбоев. ^ _ ^ Я буду беспокоиться о том, что на самом деле содержат эти данные, и как обрабатывать отброшенные пакеты и т. д. Позже, мне просто нужно сначала сделать это.

Спасибо за любую помощь / совет.

PS: Просто подумал об этом, может упростить вопрос. Есть ли способ использовать систему обработки событий Windows в моих интересах? Например, возможно ли, чтобы поток A где-то инициализировал данные, а затем запускал событие в потоке B, чтобы он собирал данные, и наоборот для потока B, чтобы сообщить потоку A, что это было сделано? Это, вероятно, решило бы многие мои проблемы, поскольку мне действительно не нужны оба потока, чтобы иметь возможность работать с данными одновременно, это скорее передача эстафеты. Я просто не знаю, возможно ли это между двумя разными потоками. (Я знаю, что один поток может создавать свои собственные сообщения для обработчика событий.)

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
4 107
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Я предлагаю изучить неблокирующие сокеты для быстрого исправления. Используя неблокирующие сокеты, send () и recv () не блокируются, а с помощью функции select () вы можете получать любые ожидающие данные в каждом кадре.

Рассматривайте это как проблема производитель-потребитель: при получении ваш сетевой коммуникационный поток является производителем, а поток пользовательского интерфейса - потребителем. При отправке все как раз наоборот. Реализуйте простой буферный класс, который предоставляет вам такие методы, как push и pop (pop должен блокировать сетевой поток и неблокировать поток пользовательского интерфейса).

Вместо использования системы событий Windows я бы предпочел что-то более портативное, например Переменные условия повышения.

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

Я думаю о них как о почтовых ящиках на поток. Вы хотите отправить пакет? Попросите ProcessThread создать «сообщение потока» с полезной нагрузкой для передачи по сети и «отправить» его в NetworkThread (т.е. протолкнуть его в очередь / почтовый ящик NetworkThread и сигнализировать переменной условия NetworkThread, чтобы он проснулся и сними это). Когда NetworkThread получает ответ, упакуйте его в сообщение потока и отправьте обратно в ProcessThread таким же образом. Разница в том, что ProcessThread не будет заблокирован для переменной условия, а просто будет опрашивать mailbox.empty (), когда вы хотите проверить ответ.

Возможно, вы захотите отправлять и извлекать файлы напрямую, но для более крупных проектов более удобным способом является реализация схемы toThreadName, fromThreadName в базовом классе ThreadMsg и почтового отделения, в котором потоки регистрируют свой почтовый ящик. Затем PostOffice отправляет сообщение (ThreadMsg *); функция, которая получает / отправляет сообщения в соответствующий почтовый ящик на основе получателя и отправителя. Почтовый ящик (класс буфера / очереди) содержит ThreadMsg * = receiveMessage (), в основном выталкивая его из базовой очереди.

В зависимости от ваших потребностей, у вас может быть ThreadMsg, содержащий процесс виртуальной функции (..), который может быть соответствующим образом переопределен в производных классах, или просто обычный класс ThreadMessage с элементами to, from и функцией getPayload () для возврата сырые данные и обрабатывать их непосредственно в ProcessThread.

Надеюсь это поможет.

Предложение BlodBath о неблокирующих сокетах - потенциально правильный подход.

Если вы пытаетесь избежать использования многопоточного подхода, вы можете исследовать использование настройки перекрывающегося ввода-вывода в ваших сокетах. Они не будут блокироваться, когда вы выполняете передачу или прием, но имеют дополнительный бонус в виде возможности ожидания нескольких событий в рамках одного цикла событий. Когда ваша передача закончится, вы получите сообщение о событии. (подробности см. в это)

Это не несовместимо с многопоточным подходом, поэтому есть возможность передумать позже. ;-)

О дизайне вашего многопоточного приложения. Лучше всего проработать все внешние действия, о которых вы хотите получать предупреждения. Например, до сих пор в вашем вопросе вы указали сетевые передачи, сетевые приемы и активность пользователей. В зависимости от количества одновременных подключений, с которыми вы собираетесь иметь дело, вы, вероятно, обнаружите, что концептуально проще иметь поток на сокет (при условии небольшого количества сокетов), где каждый поток отвечает за всю обработку этого сокета. .

Затем вы можете реализовать некоторую форму системы обмена сообщениями между вашими потоками, как было предложено RC. Организуйте свою систему так, чтобы при отправке сообщения в конкретный поток отправлялось и событие. Затем ваши потоки могут быть отправлены в спящий режим в ожидании одного из этих событий. (а также любые другие стимулы, такие как события сокета, пользовательские события и т. д.)

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

Также помните об ограничениях вашего графического интерфейса, когда дело доходит до многопоточности. Некоторое обсуждение этой темы можно найти в в этом вопросе. Но сокращенная версия состоит в том, что большинство (и Windows является одним из таких) графических интерфейсов не позволяют нескольким потокам выполнять операции графического интерфейса одновременно. Чтобы обойти эту проблему, вы можете использовать насос сообщений в своем приложении, отправив пользовательские сообщения в свой поток графического интерфейса, чтобы заставить Это выполнять операции графического интерфейса.

Ответ принят как подходящий

Самое легкое

для вас будет просто вызвать Windows API QueueUserWorkItem. Все, что вам нужно указать, - это функцию, которую будет выполнять поток, и переданный ему ввод. Пул потоков будет автоматически создан для вас и выполняемых в нем заданий. Новые потоки будут создаваться по мере необходимости.

http://msdn.microsoft.com/en-us/library/ms684957(VS.85).aspx

Больше контроля

У вас может быть более подробный контроль, используя другой набор API, который снова может управлять пулом потоков за вас -

http://msdn.microsoft.com/en-us/library/ms686980(VS.85).aspx

Сделай сам

Если вы хотите контролировать все аспекты создания потока и управления пулом, вам придется создавать потоки самостоятельно, решать, как они должны заканчиваться, сколько создавать и т. д. (Beginthreadex - это api, который вы должны использовать для создания потоков. Если вы используйте MFC, вы должны использовать функцию AfxBeginThread).

Отправка заданий в рабочие потоки - Порты завершения Io

В этом случае вам также придется беспокоиться о том, как сообщать о своих вакансиях - я бы порекомендовал IoCOmpletionPorts для этого. Это самый масштабируемый механизм уведомлений, который я в настоящее время знаю, созданный для этой цели. У него есть дополнительное преимущество, заключающееся в том, что он реализован в ядре, так что вы избегаете всевозможных ситуаций мертвой блокировки, с которыми вы можете столкнуться, если решите что-то обработать самостоятельно.

Эта статья покажет вам, как с примерами кода -

http://blogs.msdn.com/larryosterman/archive/2004/03/29/101329.aspx

Обратное общение - сообщения Windows

Вы можете использовать сообщения Windows, чтобы передать статус своему родительскому потоку, поскольку он все равно ожидает сообщения. используйте для этого функцию PostMessage. (и проверьте на наличие ошибок)

ps: вы также можете выделить данные, которые необходимо отправить, на выделенный указатель, а затем рабочий поток может позаботиться об их удалении после отправки. Таким образом, вы также избежите трафика указателя возврата.

Есть ли решение для машин, отличных от Windows?

Oliver 15.05.2012 09:12

Некоторые темы, которые могут вас заинтересовать:

  • мьютекс: мьютекс позволяет заблокировать доступ к определенным ресурсам только для одного потока

  • семафор: способ определить, сколько пользователей у определенного ресурса все еще есть (= сколько потоков обращаются к нему), и способ доступа потоков к ресурсу. Мьютекс - это особый случай семафора.

  • критическая секция: фрагмент кода, защищенный мьютексом (улица только с одной полосой движения), по которому может перемещаться только один поток за раз.

  • очередь сообщений: способ распределения сообщений в централизованной очереди

  • межпроцессное взаимодействие (IPC) - способ взаимодействия потоков и процессов друг с другом через именованные каналы, разделяемую память и многие другие способы (это скорее концепция, чем особый метод)

Все темы, выделенные жирным шрифтом, можно легко найти в поисковой системе.

Другие вопросы по теме