bambooflow Note

イベント

最終更新:

bambooflow

- view
メンバー限定 登録/ログイン

イベント

イベント(sc_event)はスレッド間の同期をとるために非常に役立ちます。
ここでは、イベント(sc_event)の使い方についてメモします。


sc_eventの使い方

簡単なsc_event使用例

以下にもっとも簡単なsc_eventの使い方を記述します。

#include <systemc.h>

SC_MODULE( TOP )
{
    sc_event   e0;    // イベント準備
 
    SC_CTOR( TOP ) {
        SC_THREAD( thread0 );
        SC_THREAD( thread1 );
    }
 
    // イベントを通知するスレッド
    void thread0() {
        cout << "--- thread0 start ---" << endl;
        while (true) {
            wait( 10, SC_NS );
 
            e0.notify();  // イベント通知
        }
    }
 
    // イベントを受け取るスレッド
    void thread1() {
        cout << "--- thread1 start ---" << endl;
        while (true) {
            wait( e0 );   // イベント待ち
 
            cout << sc_time_stamp() << " : thread1" << endl;
        }
    }
};
 
int sc_main( int argc, char* argv[] )
{
    TOP top( "top" );
 
    sc_start( 100, SC_NS );
 
    return 0;
}
 
 


  • 実行結果
--- thread0 start ---
--- thread1 start ---
10 ns : thread1
20 ns : thread1
30 ns : thread1
40 ns : thread1
50 ns : thread1
60 ns : thread1
70 ns : thread1
80 ns : thread1
90 ns : thread1

とりあえずsc_eventの動作確認するだけなので、なんの面白みもないです。
あえて言うなら、こんな処理で2つスレッドを用意するのは無駄です。
説明のためにあえて2つのスレッドを用意しました。

イベントの用途は、基本はプロセス間の同期です。

記述を見ていきます。
まず、TOPモジュールのメンバ変数として、イベントe0を用意しています。
sc_evnet e0;

thread0とthread1はSC_THREAD指定してwhile(true)文により無限ループとなっています。
thread0では、イベントを通知(発行)しています。
e0.notify();

thread1では、イベントを待っています。
wait( e0 );

イベントを受け取ると、ウェイト状態が解除され、waitの後述部分が処理されます。
後述は、ここでは単に標準出力(cout)させてます。

ようは、thread1の動作開始をthread0から操作しているようなイメージです。


今回はnotify()の引数はなにも与えていませんが、時間を指定することもできます。
たとえば、次のように記述できます。
e0.notify( 5, SC_NS );
このように記述すると、notifyが実行されてから5[ns]後にイベントが通知されることになります。

  • 注意
プロセス間のタイミングを取るために通常の変数(boolやint)を使うのは誤りです。
プロセス間のタイミングでは、必ずsc_eventもしくはチャネル(sc_signal<>やsc_buffer<>)を使わないといけません。
そうしないと、シミュレーション誤動作の原因となります。


2つのスレッドがお互いに同期をとる記述例


#include <systemc.h>

SC_MODULE( TOP )
{
    sc_event e0; // イベント宣言
    sc_event e1; // イベント宣言
 
    SC_CTOR( TOP ) {
        SC_THREAD( thread0 );
        SC_THREAD( thread1 );
    }
 
    void thread0() {
        cout << "--- thread0 start ---" << endl;
        wait( SC_ZERO_TIME );
        while (true)
        {
            e0.notify(); // イベント通知
 
            wait( e1 ); // イベントを待つ
 
            cout << sc_time_stamp() << " : thread0" << endl;
 
            wait( 10, SC_NS );
        }
    }
 
    void thread1() {
        cout << "--- thread1 start ---" << endl;
        while (true)
        {
            wait( e0 ); // イベントを待つ
 
            cout << sc_time_stamp() << " :       thread1" << endl;
 
            wait( 10, SC_NS );
 
            e1.notify(); // イベント通知
        }
    }
};
 
int sc_main( int argc, char* argv[] )
{
    TOP top( "top" );
 
    sc_start( 100, SC_NS );
 
    return 0;
}
 

  • 実行結果
--- thread0 start ---
--- thread1 start ---
0 s :       thread1
10 ns : thread0
20 ns :       thread1
30 ns : thread0
40 ns :       thread1
50 ns : thread0
60 ns :       thread1
70 ns : thread0
80 ns :       thread1
90 ns : thread0

実行結果をみると、thread0とthread1が交互に動作していることがわかります。
thread0は、イベント通知→イベント待ち→標準出力→wait(10ns)、
thread1は、イベント待ち→標準出力→wait(10ns)→イベント通知、
の順番となっています。

ここで、1点注目してほしいのは、thread0のwhile(true)文の前で、waitを1回呼んでいることです。
wait( SC_ZERO_TIME );
これは、必要です。
なぜならば、イベントの発生を知るためにはそのイベントを常に監視していなければならない、というルールがあるからです。

もし、このwait記述がないと正しく動作してくれません。でも動作することもあります。あいまいです。
それは、最初の動作時にthread0のe0.notify()とthread1のwait(e0)のどちらが先に実行されるかがあいまいだからです。
thread1のwait(e0)が先に実行されれば所望の動作しますが、thread0のe0.notify()が先に実行されてしまうと、イベント発行を見逃してしまうことになります。


誤った使い方


  • 1つのスレッド内で使おうとした例
次の記述は明らかに誤りです。
SC_MODULE( TOP )
{
    sc_event m_evnt;
 
    SC_CTOR( TOP ) {
        SC_THREAD( thread0 );
    }
    void thread0 {
        while (true) {
            m_evnt.notify();
 
            /* ...  */
 
            wait( m_evnt );
        }
    }
};
 

m_evnt.notify()にてイベントを発行していますが、それを受けるwaitの位置がおかしいです。
本来であれば別プロセスにwaitを用意しなければいけません。



まとめ


  • イベントは、2つのプロセスの同期をとるためのもの。
  • ルールとして、イベントは常に監視しなければ、イベント発行を見逃してしまう。



リファレンス


記事メニュー
目安箱バナー