1. ホーム
  2. c

[解決済み] スレッドセーフとリエントラント

2023-02-21 03:29:13

質問

最近、私はある質問をしたのですが、そのタイトルは mallocはスレッドセーフですか? というタイトルで質問し、その中で「mallocはリエントラントですか?

リエントラントはすべてスレッドセーフであるという印象を持っていました。

この仮定は間違っているのでしょうか?

どのように解決するのですか?

リエントラント関数は、Cライブラリのヘッダで公開されているグローバル変数に依存しません ... 例えば、C言語の strtok() と strtok_r() のように。

リエントラント関数は、このポインタをグローバル変数ではなく、スレッド自身のストレージに指定することができます。このストレージは呼び出した関数だけのものなので、中断されたり 再入可能 (リエントラント) であり、ほとんどの場合、関数が実装している以上の相互排除は必要ないため、しばしば スレッドセーフ . しかし、これは定義によって保証されているわけではありません。

errno は、POSIX システムでは少し異なるケースです (そして、これがどのように動作するかの説明では、変わり者扱いされる傾向があります) :)。

要するに、リエントラント はしばしば はスレッドセーフを意味しますが ("use the reentrant version of that function if you're using threads" のように)、スレッドセーフは必ずしもリエントラントを意味しません (逆もあります)。スレッドセーフを見るときは 同時実行 について考える必要があります。関数を使用するためにロックと相互排除の手段を提供しなければならないのであれば、その関数は本質的にスレッドセーフではありません。

しかし、すべての関数がそのどちらかを調べる必要があるわけではありません。 malloc() はリエントラントである必要がなく、任意のスレッドのエントリポイントの範囲外のものに依存しません(そしてそれ自身はスレッドセーフです)。

静的に割り当てられた値を返す関数は ではなく であり、ミューテックス、フーテックス、あるいは他のアトミックなロック機構を使用しない限り、スレッドセーフではありません。しかし、割り込まれないのであれば、リエントラントである必要はありません。

すなわち

static char *foo(unsigned int flags)
{
  static char ret[2] = { 0 };

  if (flags & FOO_BAR)
    ret[0] = 'c';
  else if (flags & BAR_FOO)
    ret[0] = 'd';
  else
    ret[0] = 'e';

  ret[1] = 'A';

  return ret;
}

ですから、お分かりのように、ある種のロックなしに複数のスレッドにこれを使わせるのは大失敗です...しかし、リエントラントである意味はありません。動的割り当てメモリが組み込みプラットフォームでタブーである場合、そのような事態に陥るでしょう。

純粋な関数型プログラミングでは、リエントラントはしばしば はしません。 関数エントリポイントに渡される定義済み関数や無名関数、再帰などの動作に依存します。

スレッドセーフ」をより良く表現する方法は 同時アクセスに対して安全である の方が必要性をよく表しています。