C++でプログラムを書いています。間隔を置いて何かを行うことを目的としたスレッドがいくつか増えていることに気付きました.3つか4つありました. これらのスレッドを使用する他の場所がサブスクライブできるスケジューラー サービスを作成することでリファクタリングすることにしました。
これを使用するコードはまだありません。書き始める前に、それが可能かどうかを知り、私のデザインについてフィードバックを得たいと思います。私が達成したいことの簡単な説明はこれです:
イベントを追加するには
- 発信者はイベントとスケジュールを提供します
- スケジュールは、イベントの次の発生を提供します
- (イベント、スケジュール) ペアがイベント キューに追加されます
- スリープ状態のイベント スレッドに割り込む (つまり、ウェイク アップ)
イベントスレッドのメインループ
- イベント キューで次のイベントを取得しようとする
- 保留中のイベントがない場合は、そのまま 4 に進みます
- 次のイベントが発生する予定の時刻を取得する
- 次のイベントまでスリープします (待機イベントがない場合は永久に)
- 何らかの理由で睡眠が中断された場合は、1 にループバックします
- スリープが正常に完了した場合は、現在のイベントを実行します
- キューを更新します (イベントを削除し、繰り返しイベントの場合は再挿入します)
- 1 に戻る
私は少し調査を行い、スリープ状態のスレッドに割り込むことができることを知っています。また、イベント キューへの同時アクセスが防止されている限り、危険な動作はないと考えています。スレッドのスリープ解除は可能であると想像します。Java の Thread の sleep() 呼び出しは状況によっては InterruptedException をスローし、オペレーティング システムの基になるスリープ呼び出しに依存しない限り、何らかの方法で可能になるはずです。
質問
誰でも私のアプローチについてコメントできますか? これは、再発明しないほうがよい車輪ですか? 具体的には、次の命令で実行が再開されるように、スリープ状態のスレッドをどのように中断できますか?また、中断されたスレッドからこれを検出することは可能ですか?
ブーストについての注意
ブーストを使用してスケジューラを作成できると思いますが、これは、より良いフレーズがないため、がらくたの負荷であるマシンでコンパイルおよび実行されます。以前にブースト プログラムをコンパイルしたことがありますが、通常、ブーストを取り込む各ファイルのコンパイルには 30 秒以上かかります。この苛立たしい開発障害を回避できるのであれば、是非回避したいです。
補遺 - 作業コード [caf の提案により修正]
これは私が作成したコードです。初歩的なテストが行われていますが、さまざまな遅延を伴う単一イベントと繰り返しイベントの両方を適切に処理しています。
イベント スレッドの本文は次のとおりです。
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
サブクラスは overrideaction::DoEvent
です。このメソッドは内部から呼び出されますEvent::DoEvent
。 actions
イベントによって「所有」されます。つまり、イベントのスケジュールを変更する必要がなくなった場合、それらは自動的に削除されます。