Пишу программу на С++. Я заметил, что он набирает несколько потоков, цель которых - делать что-то через определенные промежутки времени, их 3 или 4. Я решил провести рефакторинг, написав службу планировщика, на которую могли подписаться другие места, использующие эти потоки, что должно уменьшить количество дополнительных потоков событий, которые я запускаю в любое время, до одного.
У меня пока нет кода, использующего это; прежде чем я начну писать, я хотел бы знать, возможно ли это, и получить отзывы о моем дизайне. Краткое описание того, что я хотел бы сделать, это:
Чтобы добавить событие
Основной цикл потока событий
Я провел небольшое исследование и знаю, что можно прервать спящий поток, и я считаю, что до тех пор, пока одновременный доступ к очереди событий предотвращен, не должно быть никакого опасного поведения. Я полагаю, что пробуждение потока ДОЛЖНО быть возможным, вызов Java Thread sleep ()при некоторых обстоятельствах вызывает InterruptedException, и если он не полагается на базовый вызов сна операционной системы, это должно быть возможно каким-то образом.
Кто-нибудь может прокомментировать мой подход? Это колесо, которое мне лучше не изобретать? Как, в частности, вы можете прервать спящий поток, чтобы выполнение возобновилось со следующей инструкции, и можно ли обнаружить это в прерванном потоке?
Примечание о форсировании
Могу поспорить, что вы можете написать планировщик с boost, но он компилируется и работает на машине, которая, за неимением лучшего выражения, является кучкой дерьма. Я уже компилировал на нем программы boost, и компиляция каждого файла, который загружает boost, обычно занимает более 30 секунд. Если бы я мог избежать этого раздражающего препятствия развития, я бы очень хотел.
Это код, который я создал, который работает. Он был предварительно протестирован, но правильно обрабатывал как одиночные, так и повторяющиеся события с различной задержкой.
Вот тело потока событий:
void Scheduler::RunEventLoop()
{
QueueLock(); // lock around queue access
while (threadrunning)
{
SleepUntilNextEvent(); // wait for something to happen
while (!eventqueue.empty() && e.Due())
{ // while pending due events exist
Event e = eventqueue.top();
eventqueue.pop();
QueueUnlock(); // unlock
e.DoEvent(); // perform the event
QueueLock(); // lock around queue access
e.Next(); // decrement repeat counter
// reschedule event if necessary
if (e.ShouldReschedule()) eventqueue.push(e);
}
}
QueueUnlock(); // unlock
return; // if threadrunning is set to false, exit
}
Вот функция сна:
void Scheduler::SleepUntilNextEvent()
{
bool empty = eventqueue.empty(); // check if empty
if (empty)
{
pthread_cond_wait(&eventclock, &queuelock); // wait forever if empty
}
else
{
timespec t = // get absolute time of wakeup
Bottime::GetMillisAsTimespec(eventqueue.top().Countdown() +
Bottime::GetCurrentTimeMillis());
pthread_cond_timedwait(&eventclock, &queuelock, &t); // sleep until event
}
}
Наконец, AddEvent:
void Scheduler::AddEvent(Event e)
{
QueueLock();
eventqueue.push(e);
QueueUnlock();
NotifyEventThread();
}
Соответствующие объявления переменных:
bool threadrunning;
priority_queue<Event, vector<Event>, greater<Event> > eventqueue;
pthread_mutex_t queuelock; // QueueLock and QueueUnlock operate on this
pthread_cond_t eventclock;
Чтобы решить проблему общих событий, каждый Event
содержит указатель на объект абстрактного типа action
, подклассы которого переопределяют action::DoEvent
. этот метод вызывается изнутри Event::DoEvent
. actions
«принадлежат» своим событиям, т. е. они автоматически удаляются, если событие больше не нужно перепланировать.