4

これは、「単純に明白ですが、おそらく間違っている」カテゴリに分類されるように思われる質問の1つです。確かに、私はすべてのコーナーケースで機能する解決策を見つけるのに苦労しています。これは常に遭遇する問題のようです。

「ハードウェア」スレッドと「処理」スレッドがあります。

ハードウェアスレッドは、私がアクセスできない閉じたバイナリライブラリで実行されます。

処理スレッドは、ハードウェアの状態変化に関連する特定の(まれな)イベントを通知するために、ハードウェアスレッドにコールバックを登録します。

状態が変化したことを処理スレッドのイベントループに通知できるようにしたいと思います。外部ライブラリに依存したり、移植性のない方法で、ロックを使用せずにこれを実行したいと思います(ハードウェアスレッドがいつ処理スレッドに再度通知するかわからないため)。

それで、これをどのように解決するかについての現在の私の考えは次のようなものです:

#include <signal.h>

// sig_atomic_t is used so update is always in a sane state
static volatile sig_atomic_t update = 0;

// called from hardware thread
int callback_function() {
    update += 1;
}

// called regularly from processing thread
int processing_function() {
    static sig_atomic_t local_update = 0; // The same type as update

    if (local_update != update){
        update_internal_hardware_state(); // We necessarily call once per callback
        local_update += 1;
    }
}

明らかに、これがupdateラップアラウンドし、次に呼び出されるlocal_update前の値にたまたま到達した場合、これは壊れますprocessing_function(ただし、ほぼ確実に、これは決して発生しないと想定できます)。

ここで微妙な(またはそれほど微妙ではない)何かを見逃したことがありますか?

この問題全体を解決するためのより良い方法はありますか?

コールバックは、1つのスレッド(ハードウェアスレッド)からのみ呼び出されると想定できます。

4

3 に答える 3

3

1つのオプションは、標準のUNIXセルフパイプトリックを使用することです。利点は、アトミック変数の値を定期的にポーリングすることなく、パイプの読み取り端をイベントループと一緒に使用したり、イベントループとうまく統合しselect()たりできることです。epoll()

#include <unistd.h>
#include <errno.h>

int self_pipe[2]; // the read end is non-blocking

// called from hardware thread
int callback_function() {
    write(self_pipe[1], "", 1); // notify by writing a byte
    return 0;
}

// called only when self_pipe[0] is ready for read
int processing_function() {
    for(;;) {
        char buf;
        ssize_t n = read(self_pipe[0], &buf, 1);
        if(n > 0) {
            // callback_function() has been called
        }
        else if(n < 0) {
            if(EAGAIN == errno)
                break;
            // handle an (unlikely) error
        }
        else {
            // handle pipe EOF if necessary
        }
    }
    return 0;
}

Linuxを使用している場合は、パイプの代わりに2.6.22以上を使用できますeventfd。これは、基本的にカーネルで維持され、read()syscallwrite()で操作されるセマフォです。繰り返しになりますが、この利点は、イベントループで使用できることです。

于 2011-10-18T07:50:18.147 に答える
2

コンペアアンドスワップ操作にアクセスできますか?次に、処理スレッドで次のようなことを行うことができます。

int ticks_since_last_check = 0, ticks_ret = 0;
do {
    ticks_since_last_check = ticks_ret;   
    ticks_ret = compare_and_swap(&update, ticks_since_last_check, 0));
} while (ticks_since_last_check != ticks_ret)

これにより、処理スレッドが値をチェックするたびにティックカウンターが0にリセットされるため、カウンターがオーバーフローすることはありません。update_internal_hardware_stateその後、関数時間を実行する必要がありますticks_since_last_check

または、アトミックデクリメントを使用できます。

int ticks_since_last_check = 0;
while (atomic_read(&update) != 0) {
    atomic_decrement(&update);
    ++ticks_since_last_check;
}
于 2011-10-18T02:33:22.403 に答える
2

セマフォを手動で実装したようです。

代わりに、プラットフォームに適切なセマフォを使用してみませんか?PosixセマフォはARMプラットフォームに存在します。なぜ外部ライブラリを使用したくないのかわかりません。自分で使用することを主張する場合は、これが役立つ可能性があります

于 2011-10-18T04:33:15.840 に答える