言語とプラットフォームを知らなければ、詳細な実装についてアドバイスするのは少し難しいですが、私のアドバイスはLiorKoganのアドバイスと多少似ています。ただし、私の意見では、必要なセットは2つだけで、マップは含まれていません。
セットを表す2つの変数、AとBがあるとします。
ハートビートごとにセットAからエージェントIDが削除されます。5秒ごとに、異なるスレッドがB内のすべてのエージェントIDに対してアラートを生成し、次にB = Aに設定します。最後に、すべてのエージェントIDとセットAを含むセットを作成します。これに等しい(エージェントIDの数が非常に多い場合は、1つのチェックと別のチェックの間に新しいセットを準備し、残りの時間だけスリープすることができます)。ロックフリーのセットコレクションを使用する場合、ロックは各セットを指す変数を変更するときにのみ必要になります。パフォーマンスは、前述の実装のアルゴリズムの複雑さに大きく依存します。このように下げる場合は、パフォーマンスが最高の1つ(たとえば、最悪の場合の遅延が重要な場合など、必ずしも最高のbig-Oである必要はありません)を削除する特権を与える必要があります。
ちなみに、メモリが問題ではない場合、または障害が比較的少ない場合は、アラートを発生させる必要があるかどうかを確認するときに、独自のスレッドでそれを実行して、おそらく興味深いパフォーマンスのスピードアップを得ることができます(ここでも、プラットフォームとランタイムの問題。erlangでは簡単ですが、Windowsでは、古いBセットをメモリに保持するという犠牲を払って、本格的な新しいスレッドを作成するコストがパフォーマンス上の利点を超える可能性があります。