私のコードはできるだけシンプルでスレッドセーフであることが必要です。そのISO/IEC 9899/201XドラフトフェンスでSPSCスレッドを安全にする
XとYの "7.17.4フェンス" の部分についてはC11のアトミック
で
、両方のいくつかのアトミックオブジェクトM上で動作する、そのようなAは がXの前に配列され、XがMより前に配列され、YがBより前に配列され、Yが のXで書かれた値または の操作でリリースされた場合、Yが 仮想放出配列X 。
このコードはスレッドセーフです(「オブジェクトM」の「w_i」)。
"w_i"と "r_i"の両方を_Atomicとして宣言する必要がありますか?
w_iのみが_Atomicの場合、メインスレッドはr_iの古い値をキャッシュに保持し、キューがいっぱいではないとみなし(フルの間)、データを書き込むことはできますか?
atomic_loadのないアトミックを読み込むとどうなりますか?
私はいくつかのテストを行いましたが、私の試みはすべて正しい結果を出すようです。 しかし、私はマルチスレッドに関して私のテストが本当に正しくないことを知っています。プログラムを何度か実行して結果を見ます。
どちらw_iは_atomic、私のプログラムの仕事としてR_iと宣言されていないが、唯一のフェンスは、右、C11の規格に関しては十分でない場合であっても?
typedef int rbuff_data_t;
struct rbuf {
rbuff_data_t * buf;
unsigned int bufmask;
_Atomic unsigned int w_i;
_Atomic unsigned int r_i;
};
typedef struct rbuf rbuf_t;
static inline int
thrd_tryenq(struct rbuf * queue, rbuff_data_t val) {
size_t next_w_i;
next_w_i = (queue->w_i + 1) & queue->bufmask;
/* if ring full */
if (atomic_load(&queue->r_i) == next_w_i) {
return 1;
}
queue->buf[queue->w_i] = val;
atomic_thread_fence(memory_order_release);
atomic_store(&queue->w_i, next_w_i);
return 0;
}
static inline int
thrd_trydeq(struct rbuf * queue, rbuff_data_t * val) {
size_t next_r_i;
/*if ring empty*/
if (queue->r_i == atomic_load(&queue->w_i)) {
return 1;
}
next_r_i = (queue->r_i + 1) & queue->bufmask;
atomic_thread_fence(memory_order_acquire);
*val = queue->buf[queue->r_i];
atomic_store(&queue->r_i, next_r_i);
return 0;
}
私は次のように論文の関数を呼び出す:
メインスレッドがいくつかのデータをエンキュー:デキューデータのスレッド
while (thrd_tryenq(thrd_get_queue(&tinfo[tnum]), i)) {
usleep(10);
continue;
}
その他:ASMフェンス
で
static void *
thrd_work(void *arg) {
struct thrd_info *tinfo = arg;
int elt;
atomic_init(&tinfo->alive, true);
/* busy waiting when queue empty */
while (atomic_load(&tinfo->alive)) {
if (thrd_trydeq(&tinfo->queue, &elt)) {
sched_yield();
continue;
}
printf("Thread %zu deq %d\n",
tinfo->thrd_num, elt);
}
pthread_exit(NULL);
}
、LFENCEとSFENCEと特定のプラットフォームのx86に関しては、私はすべてのC11コードを削除し、ちょうど
asm volatile ("sfence" ::: "memory");
と
asm volatile ("lfence" ::: "memory");
でフェンスを交換した場合(これらのマクロの私の理解では、次のとおりです。メモリを防ぐために、コンパイラフェンス再利用/最適化+ハードウェアフェンスへのアクセス)
私の変数は、たとえばvolatileとして宣言する必要がありますか?
私はすでにこれらのみASMフェンス付き超えるが無い原子の種類と、このリングバッファコードを見ていると私は本当に驚いた、私はこのコードが正しかったかどうかを知りたいです。
ありがとうございました!私がフェンスを避けるならば、私はatomic_store_explicitをメモリオーダーに使用する必要がありますか?または、私はちょうど書くだろう:キュー - > w_i = next_w_i(それはデフォルトで使用される緩やかな順序ですか?) 私はフェンスを使用する必要がありますか? – treywelsh
いいえ、デフォルトの順序は、順次整合性であり、可能な限り最強です。 –