#c #multithreading #inheritance #this
#c #многопоточность #наследование #это
Вопрос:
Каков наилучший способ выполнить следующее в C . Хотя мой текущий метод работает, я не уверен, что это лучший способ:
1) У меня есть мастер-класс, в котором есть некоторая функция
2) У меня есть поток, который выполняет некоторые инструкции в сокете, а затем запускает одну из функций в мастер-классе
3) Существует несколько потоков, которые обращаются к различным функциям в мастер-классе
Я создаю мастер-класс, а затем создаю экземпляры классов потоков из мастера. Конструктору для класса thread передается указатель «this» для ведущего. Затем я могу запускать функции из мастер-класса внутри потоков, то есть я получаю команду для выполнения чего-то, что запускает функцию в мастер-классе из потока. У меня есть мьютексы и т.д., чтобы предотвратить проблемы с гонкой.
Я делаю это неправильно — кажется, что классы потоков должны наследовать мастер-класс, или другой подход заключался бы в том, чтобы не иметь отдельных классов потоков, а просто использовать их как функции мастер-класса, но это становится уродливым.
Комментарии:
1. Не могли бы вы опубликовать какой-нибудь код? Знаете ли вы о различии между классами и экземплярами? Существует ли один экземпляр мастер-класса для каждого потока?
2. Нет, существует один мастер-класс, из которого создаются потоки. Потокам просто нужно получить доступ к основным функциям-членам и некоторым переменным
Ответ №1:
Звучит хорошо для меня. На моих серверах он называется ‘SCB’ — ServerControlBlock — и предоставляет доступ к таким службам, как пулы IOCPbuffer / socket, регистратор, доступ к пользовательскому интерфейсу для сообщений о состоянии / ошибках и всему остальному, что должно быть общим для всех потоков обработчиков. Работает нормально, и я не рассматриваю это как взлом.
Я создаю SCB (и гарантирую в ctor, что все службы, к которым осуществляется доступ через него, запущены и готовы к использованию), прежде чем создавать пул потоков, который использует SCB — никаких неприятных одиночных вещей.
Rgds, Мартин
Комментарии:
1. Хорошо, звучит хорошо — я продолжу использовать «это» в моем пуле потоков.
Ответ №2:
Отдельные классы потоков довольно нормальны, особенно если они обладают определенной функциональностью. Я бы не стал наследовать от основного потока.
Комментарии:
1. Да, я думаю, главный вопрос, который у меня есть, — это передача указателя «this» на классы потоков с плохим кодированием или он довольно стандартный. Это похоже на небольшой взлом
2. Это не взлом, если вы рассматриваете мастер-класс как одноэлементный и т. Д.
3. Передача указателя «this» — это нормально, это обычное дело. Как сказал Адитья, вы также можете сделать свой основной класс одноэлементным, чтобы вы вообще не передавали никаких указателей, вы просто вызывали MainClass::getInst()-> когда бы вы ни хотели получить к нему методы доступа.
Ответ №3:
Передача this
указателя на потоки сама по себе не является плохой. То, что вы делаете с этим, может быть.
this
Указатель такой же, как и любой другой тип данных POD-ish. Это просто кусок битов. Однако содержимое, которое находится в this
, может быть больше, чем модули, и передача того, что фактически является указателем на его элементы, может быть опасным по всем обычным причинам. Каждый раз, когда вы делитесь чем-либо между потоками, это приводит к потенциальным условиям гонки и взаимоблокировкам. Элементарным средством разрешения этих конфликтов, конечно, является введение синхронизации в виде мьютексов, семафоров и т. Д., Но Это может иметь неожиданный эффект сериализации вашего приложения.
Допустим, у вас есть один поток, считывающий данные из сокета и сохраняющий их в синхронизированном командном буфере, и другой поток, который считывает из этого командного буфера. Оба потока используют один и тот же мьютекс, который защищает буфер. Все хорошо, верно?
Ну, может быть, и нет. Ваши потоки могут стать сериализованными, если вы не будете очень осторожны с тем, как вы блокируете буфер. Предположительно, вы создали отдельные потоки для кодов вставки буфера и удаления буфера, чтобы они могли выполняться параллельно. Но если вы блокируете буфер при каждой вставке и каждом удалении, то одновременно может выполняться только одна из этих операций. Пока вы записываете в буфер, вы не можете читать из него и наоборот.
Вы можете попытаться точно настроить блокировки, чтобы они были как можно более краткими, но пока у вас есть общие синхронизированные данные, у вас будет некоторая степень сериализации.
Другой подход заключается в явной передаче данных другому потоку и удалении как можно большего общего доступа к данным. Например, вместо записи в буфер и чтения из него, как в приведенном выше примере, ваш код сокета может создать какой-то Command
объект в куче (например Command* cmd = new Command(...);
) и передать его другому потоку. (Один из способов сделать это в Windows — через механизм QueueUserAPC).
У обоих подходов есть свои плюсы и минусы. Преимуществом метода синхронизации является то, что он несколько проще для понимания и реализации на поверхности, но потенциальным недостатком является то, что его гораздо сложнее отлаживать, если вы что-то напутаете. Метод передачи данных может сделать многие проблемы, связанные с синхронизацией, невозможными (тем самым фактически упрощая ее), но для выделения памяти в куче требуется время.