// Глобальный объект (выше объявлен как extern ReportCollector gReports)
ReportCollector gReports;
void main()
{
/* Создаем очередь таймеров и несколько таймеров с заданиями (TimerRoutine) */
/* Создаем таймер с процедурой ShowReportRoutine */
system("pause"); // Ждем пока не надоест.
}
Здравствуйте, wint, Вы писали:
W>У меня мало опыта написания многопоточных приложений, по этому хочу узнать мнение сообщества, является ли такой код потокобезопастным (thread safe):
Он потоко-безопасный, но не потоко-дружественный. Пока поток выводит отчёты, все остальные потоки стоят и не могут добавлять отчёты.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, wint, Вы писали:
W>>У меня мало опыта написания многопоточных приложений, по этому хочу узнать мнение сообщества, является ли такой код потокобезопастным (thread safe):
R>Он потоко-безопасный, но не потоко-дружественный. Пока поток выводит отчёты, все остальные потоки стоят и не могут добавлять отчёты.
R>
А как сделать что был потоко-дружественным? (или где об этом почитать)
Здравствуйте, wint, Вы писали:
W>Здравствуйте, remark, Вы писали:
R>>Здравствуйте, wint, Вы писали:
W>>>У меня мало опыта написания многопоточных приложений, по этому хочу узнать мнение сообщества, является ли такой код потокобезопастным (thread safe):
R>>Он потоко-безопасный, но не потоко-дружественный. Пока поток выводит отчёты, все остальные потоки стоят и не могут добавлять отчёты.
W>А как сделать что был потоко-дружественным? (или где об этом почитать)
Навскидку, я бы сделал примерно так:
class ReportCollector
{
private:
std::vector<Report*> qReports;
std::vector<Report*> qReportsCache;
boost::mutex mLock;
public:
void AddReport(Reprot* _report)
{
boost::mutex::scoped_lock lock(mLock);
qReports.push_back(_report);
}
void Show()
{
{
boost::mutex::scoped_lock lock(mLock);
qReports.swap(qReportsCache);
}
for (size_t i = 0; i != qReportsCache.size(); i += 1)
{
std::auto_ptr<Report> r (qReportsCache[i]);
std::wcout << L"Task result: " << r->result << std::endl;
}
qReportsCache.clear();
}
~ReportCollector()
{
for (size_t i = 0; i != qReports.size(); i += 1)
delete qReports[i];
for (size_t i = 0; i != qReportsCache.size(); i += 1)
delete qReports[i];
}
};
void CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
...
std::auto_ptr<Report> r (new Report());
r->result = result;
gReports.AddReport(r.release());
}
А зачем слепок массива сделан членом класса?
Тем самым ты сломал потокобезопасность — точнее, засушил её до вида N producers 1 consumer. (Т.е. нельзя делать Show из двух потоков).
Ну и лишние данные в состояние класса занёс.
Есть смысл сделать qReportsCache локальной переменной метода Show.
Или это борьба за экономное использование кучи?
Тогда можно вот такой сон разума
Здравствуйте, Кодт, Вы писали:
К>А зачем слепок массива сделан членом класса? К>Тем самым ты сломал потокобезопасность — точнее, засушил её до вида N producers 1 consumer. (Т.е. нельзя делать Show из двух потоков). К>Ну и лишние данные в состояние класса занёс. К>Есть смысл сделать qReportsCache локальной переменной метода Show. К>Или это борьба за экономное использование кучи?
Идея — убрать ненужные обращения к менеджеру кучи и копирования. В устоявшемсе режиме оба контейнера доростут до максимального размера и просто будут обмениваться.
Насколько я понял задачу — поток, выводящий отчёт, один.
К>Тогда можно вот такой сон разума
Если выводящих потоков много, то тут тоже не будет устоявшегося режима — постоянно будут выделения и освобождения памяти.
Если требуется поддержка нескольких выводящих потоков, то я бы сделал так:
class ReportCollector
{
private:
boost::mutex mLock;
std::vector<Report*> qReports;
public:
void AddReport(Reprot* _report)
{
boost::mutex::scoped_lock lock(mLock);
qReports.push_back(_report);
}
void GetReports(std::vector<Report*>& reports)
{
reports.clear();
boost::mutex::scoped_lock lock(mLock);
qReports.swap(reports);
}
~ReportCollector()
{
for (size_t i = 0; i != qReports.size(); i += 1)
delete qReports[i];
}
};
Дальше каждый поток волен сам кэшировать или не кэшировать пустой вектор.
Если используется кэширование, то потребление памяти в устоявшемсе режиме — (P+1)*N, P — кол-во потоков, N — кол-во отчётов за период.