17

XSH 2.9.1では、wctombスレッドセーフである必要のない関数の 1 つとしてリストされています。ただし、逆の変換関数mbtowcはリストに表示されません。シフト状態を使用するエンコーディングを使用する実装では、どちらにもスレッドセーフな API がなく、一方がスレッドセーフである必要があり、もう一方がそうでないということは意味がありませんが、どちらもステートフルエンコーディングを禁止しない限りスレッドセーフにはなりません。

wcstombs(リストにある) と(ない) についても同様ですmbstowcs。これらの関数は両方とも、最初のシフト状態で開始および終了する文字列全体で動作するため、ステートフルではなく、それらの API はスレッド セーフです。他の。

誰でもこれに光を当てることができますか?

4

4 に答える 4

3

質問で指摘したようwctombに、(隠された)「シフト状態」を持っているか、少なくとも許可されています:http://pubs.opengroup.org/onlinepubs/009695399/functions/wctomb.htmlを参照して、これをwcrtomb: http://pubs.opengroup.org/onlinepubs/009695399/functions/wcrtomb.htmlには、明示的な「状態」ポインターがあります。

POSIX では基本的に、プログラマーは、変数を使用してシフト状態を保持するためwctombの呼び出しとして実装できます。その変数がスレッドごとの項目でない場合、スレッドセーフではありません。wcrtombstatic

(これはすべて非常に明白であり、あなたの質問に含まれています。ここで明確にするために繰り返しています)

wctomb隠しシフト状態 (存在する場合) を明示的に制御するための引数はありません。具体的には、開始状態にリセットすることはできません。

しかし、今見てくださいmbtowc: http://pubs.opengroup.org/onlinepubs/009695399/functions/mbtowc.htmlテキストには次のように書かれています:

状態依存エンコーディングの場合、この関数は、文字ポインタ引数 s が null ポインタである呼び出しによって初期状態に置かれます。ヌルポインター以外の s を使用した後続の呼び出しでは、関数の内部状態が必要に応じて変更されます。s を null ポインターとして呼び出すと、この関数は、エンコーディングに状態依存性がある場合はゼロ以外の値を返し、それ以外の場合は 0 を返します。実装で特殊なバイトを使用してシフト状態を変更する場合、これらのバイトは個別のワイド文字コードを生成せず、隣接する文字とグループ化する必要があります。

つまり、非表示の状態 (存在する場合) を検出して制御する明示的な方法が提供されます。したがって、それが存在し、スレッド固有ではない場合でも、いわば「独自の制御を行う」ことができます。

私はそれを誓うことはできませんが、これがmbtowc非スレッドセーフとしてリストされていない理由だと思います.

(しかし、これは私が標準でテキストを書く方法ではありません! また、私が私のやり方を持っていれば、これらの関数の多くは存在さえしないでしょう. :-) )

于 2012-03-13T07:15:57.613 に答える
2

基本的な非対称性は、ISO C ではワイド文字の幅が固定されていること (すべての文字で同じ) と、エンコーディングにシフト状態がないことが要求されていることです。対照的に、マルチバイト エンコーディングはロケールに依存し、文字幅やシフト状態が異なる場合があります。

4 つの関数はすべて、呼び出し間で保持される内部状態を持っています (mbstowcsまたwcstombs、最初のシフト状態で終了する完全な文字列とは対照的に、指定されたバイト数のみを変換するため、保持する必要があります)。

文字列変換の場合、内部状態の構成要素に微妙な違いがあります。の場合mbstowcs、ワイド文字の整数が 1 回の呼び出しで変換されます。これは、ワイド文字の幅が固定されていることnと、呼び出しのパラメーターがバイト単位ではなく文字単位で指定されているためです。対照的にwcstombsnパラメーターはマルチバイト文字ではなくバイト単位で指定されます。したがって、保持される状態にwcstombsは、シフト状態だけでなく、部分的に出力されたマルチバイト文字の残りの部分も含める必要があります。このように状態は複数の部分に分かれているため、その状態に対する操作 (ロードとストア) は、追加のロックなしでは典型的なアーキテクチャではアトミックにはなりません。

この時点で、"スレッド セーフ" は POSIX ではかなり技術的な意味を持っていること、つまり、並列呼び出しは論理的にシリアル化可能であることを思い出すことが重要です。並列使用が必ずしも非常に役立つというわけではありません。4 つの関数はすべて内部状態を保持するため、(一度に) 1 つの線形文字列を左から右に処理し、呼び出しを複数のスレッドに分散する呼び出し元を想像するのは困難です。これは、ISO C 89/90 の Amendment 1のmbrtowcwcrtombmbsrtowcsおよびの導入によって証明されており、フラグは特に「再入可能」を表しています。wcstombsr

「原子的にアクセス可能な」内部状態を持つことで、それぞれの呼び出しをスレッドセーフな方法で実装しやすくする理由を正確に説明することはできません (1 回の呼び出し中に複数のアクセス、ロード、ストアが必要になる場合があるため)。しかし、おそらくそれは、追加のロック (およびリロード) の負担が、実際のシフト状態が発生しているほとんどアクセスされないコード ブランチにのみ課されるためです。

説明する別のキャッチもあります。並行スレッドは、ロケールsetlocaleの文字エンコーディング ( ) カテゴリの変更を呼び出すことができます。LC_CTYPEISO C 標準では、そのようなアクションによって現在の状態 (および、 を使用して適切にキャプチャされた状態でさえもwcrtomb) が未定義になることが規定されています。これは、異なるロケールのシフト状態が、有用な方法または指定された方法で互いにマッピングされない可能性があるためです。これはスレッド化されたシナリオであり、"再入可能" ファミリの関数でさえ壊れる可能性がありますが、ロケール設定は呼び出しごとにキャッシュできるため、正式にスレッドセーフな実装に必ずしも障害をもたらすわけではありません。

于 2012-03-19T22:32:37.653 に答える
1

C プログラムを「Unicode 対応」にする一般的な方法はwchar_t、代わりにcharどこでも使用し、ナロー文字標準ライブラリ関数のワイド文字バージョンを呼び出すことです。タイプの変数がオブジェクトまたはワイド文字列wchar_t*のいずれかを指すことはすぐに明らかですが、タイプの変数はオブジェクト、ネイティブ char エンコーディングのナロー文字列、またはいずれかのマルチバイト文字列を指すことができるため、私はこのアプローチが好きです。サポートされている数十の文字エンコーディング。非常に多くの根本的に異なる意味を持つwchar_tchar*charchar*、たとえば、UTF-8 でエンコードされたマルチバイト文字列をナロー文字列を期待する関数に渡したり、現在のエンコーディングのマルチバイト文字列を UTF-8-エンコードされた文字列。おそらくそれを保証するという考えmbtowcmbstowcs(マルチバイト文字列をワイド文字列に変換する関数) はスレッド セーフですが、ワイド文字列からマルチバイト文字列に変換する関数はそうではありません。これは、プログラム メモリ内の文字列データを、各文字が正確に表すワイド文字形式で常に保持するようにプログラマーを説得するためです。実行文字セットの 1 つのメンバーであり、おそらく異なる文字エンコーディングを使用するナロー文字列とマルチバイト文字列の混合ではありません。おそらく、標準的なライターは、それがより有用なアプローチである、またはより一般的であると考えていました.

マルチスレッドの Unicode 対応サーバー ソフトウェアを C で作成することを検討している場合、文字列データをワイド文字列形式で保持するパターンに従うと、「ワイヤから」読み取られた文字列データとプログラム メモリ内の文字列データを確実に分離するのに役立ちます。文字列データのペイロードを含む新しいメッセージが着信するたびに、メッセージとその文字列ペイロードを解析する C ルーチンは、マルチバイト文字からワイド文字への変換関数を含むナロー文字 I/O 関数を使用して、文字列をプログラム メモリに読み取ることができます。mbstowcs複数のスレッドが着信メッセージを解析する場合 (通常はそうです) 、 がスレッドセーフであることが強く望まれます。

于 2011-01-28T02:13:58.397 に答える
1

これは、一連のワイド文字でエンコードされたデータを使用すると、コードポイントの幅が固定されているため、プログラマーがスレッドごとにどれだけのメモリを割り当て/解放する必要があるかを予測できるという仮定に基づいていると思います。しかし、エンコーディングによっては、他の方向に進む場合、事前に割り当てる必要があるメモリの量が「予測可能」ではない可能性があるため、エラーの余地が大きくなります。

更新: 標準の古いバージョンを見つけた後、wctomb() 関数のマニュアル ページの文言に違いがあることに気付きました。再入可能であることは、スレッドセーフである必要はありません。」これは、標準で行われた別の暗黙の仮定を示唆していると思います: mbtowc() は再入可能であるか、再入可能である必要があります...

于 2011-01-28T02:17:33.123 に答える