Источники
Во-первых, вы должны прочитать все статьи о параллелизме Херба Саттера:
http://herbsutter.com/2010/09/24/effective-concurrency-know-when-to-use-an-active-object-instead-of-a-mutex/
Это ссылка на пост последней статьи, который содержитссылки на все предыдущие статьи.
Каков ваш случай?
Согласно следующей статье: Сколько масштабируемости у вас есть или нужно? (http://drdobbs.com/parallel/201202924), вы в деле O(K): Fixed
.То есть у вас есть фиксированный набор задач, которые должны выполняться одновременно.
По описанию вашего приложения у вас есть 5 потоков, каждый из которых выполняет совершенно разные задачи, поэтому вы должны иметь 5 потоков, возможно,надеясь, что один или несколько из них все еще могут разделить свои задачи на несколько потоков (и, следовательно, используя пул потоков), но это было бы бонусом.
Я позволю вам прочитать статью для получения дополнительной информации.
Вопросы по дизайну
О синглтоне
Забудьте о синглтоне.Это тупой, чрезмерно используемый шаблон .
Если вы действительно действительно хотите ограничить количество экземпляров вашего класса (и серьезно, разве у вас нет ничего лучше, чем это?), Вы должны разделить дизайн на две части: один класс для данных и один класс, чтобы обернуть предыдущий класс в одноэлементное ограничение.
О единицах компиляции
Сделайте ваши заголовки и источники легко доступными.читать.Если вам нужно иметь реализацию класса в нескольких источниках, пусть будет так.Я называю источник соответственно.Например, для класса MyClass у меня будет:
- MyClass.hpp: заголовок
- MyClass.cpp: основной источник (с конструкторами и т. Д.)
- MyClass.Something.cpp: обработка исходного кода с чем-либо
- MyClass.SomethingElse.cpp: обработка исходного кода с чем-то другим
- и т. Д.
Об оптимизации компилятора
Последние компиляторы могут встроить код из разных модулей компиляции (я видел эту опцию в Visual C ++ 2008, IIRC).Я не знаю, работает ли целая глобальная оптимизация хуже, чем компиляция «из одного модуля», но даже если это так, вы все равно можете разделить свой код на несколько источников, а затем включить в один глобальный источник все.Например:
- MyClassA.header.hpp
- MyClassB.header.hpp
- MyClassA.source.hpp
- MyClassB.source.hpp
- global.cpp
и затем сделайте ваши включения соответственно.Но вы должны быть уверены, что это на самом деле делает вашу работу лучше: не оптимизируйте, если вам это действительно не нужно и вы не профилировали его.
Ваш случай, но лучше?
Ваш вопрос и комментарии говорято монолитном дизайне больше, чем о производительности или потоке, поэтому я могу ошибаться, но что вам нужно, так это простой рефакторинг.
Я бы использовал 3-й метод (один класс на поток), потому что с классами приходит private / publicдоступ, и, следовательно, вы можете использовать это для защиты данных, принадлежащих одному потоку, только сделав его закрытым.
Следующие рекомендации могут помочь вам:
1 - каждый поток долженбыть скрытым в одном нестатическом объекте
Вы можете использовать закрытый статический метод этого класса или анонимную функцию с пространством имен для этого (я хотел бы перейти к функции, но здесь я хочуполучить доступ к закрытой функции класса, поэтому я остановлюсь на статическом методе.)
Обычно функции построения потоков позволяют передавать поИнтер к функции с параметром контекста void *
, поэтому используйте его для передачи указателя this
на функцию основного потока:
Наличие одного класса на поток помогает вам изолировать этот поток и, следовательно, этот потокданные из внешнего мира: ни один другой поток не сможет получить доступ к этим данным, поскольку они являются частными.
Вот некоторый код:
// Some fictious thread API
typedef void (*MainThreadFunction)(void * p_context) ;
ThreadHandle CreateSomeThread(MainThreadFunction p_function, void * p_context) ;
// class header
class MyClass
{
public :
MyClass() ;
// etc.
void run() ;
private :
ThreadHandle m_handle ;
static void threadMainStatic(void * p_context) ;
void threadMain() ;
}
.
// source
void MyClass::run()
{
this->m_handle = CreateSomeThread(&MyClass::threadMainStatic, this) ;
}
void MyClass::threadMainStatic(void * p_context)
{
static_cast<MyClass *>(p_context)->threadMain() ;
}
void MyClass::threadMain()
{
// Do the work
}
Отказ от ответственности: это не было проверено в компиляторе.Воспринимайте это как псевдо-код C ++ больше, чем реальный код.YMMV.
2 - определение данных, которые не являются общими.
Эти данные могут быть скрыты в закрытом разделе объекта-владельца, и если они защищены синхронизацией, то эта защита является избыточной (так как данные НЕ передаются)
3 - определение данных для общего доступа
... и проверить его синхронизацию (блокировки, атомарный доступ)
4 - Каждый класс должен иметь свой собственный заголовок и источник
... и при необходимости защитить доступ к своим (общим) данным с синхронизацией
5 - максимально защитить доступ
Если одна функция используется классом и только классом и на самом деле не нуждается в доступе к внутренним объектам класса, то она может быть скрыта в анонимном пространстве имен.
Если одна переменная принадлежит только потоку, спрячьте ее в классе как член закрытой переменной.
и т.д.