web-dev-qa-db-ja.com

プログラムは同じFILE *で同時にfflush()を呼び出すことができますか?

複数のスレッドが同じ_FILE*_変数でfflush()を同時に呼び出すと、何か悪いことが起こりますか(未定義の動作、ファイルの破損など)?

明確化:ファイルを同時に書き込むという意味ではありません。同時にフラッシュすることだけを意味します。

スレッドはファイルの読み取りまたは書き込みを同時に行いません(一度に1スレッドずつ、クリティカルセクション内にファイルを書き込むだけです)。それらはクリティカルセクションの外側にフラッシュするだけで、クリティカルセクションをより早く解放して、他の人が他の作業(ファイルの書き込みを除く)を実行できるようにします。

1つのスレッドが(クリティカルセクションの内側で)ファイルを書き込んでいるときに、別のスレッドが(クリティカルセクションの外側で)ファイルをフラッシュしている場合があります。

43
Serge Rogatch

Cでのストリーム1 スレッドセーフです2。ストリームにアクセスする前にストリームをロックするための関数が必要です3

フラッシュ関数はスレッドセーフであり、ストリームが出力ストリームまたは更新ストリームである限り、いつでも任意のスレッドから呼び出すことができます。4


1 現在の標準であるC11による。

2 (引用元:ISO/IEC 9899:201x 7.21.3 Streams 7)
各ストリームには、実行の複数のスレッドがストリームにアクセスするときのデータ競合を防ぎ、複数のスレッドによって実行されるストリーム操作のインターリーブを制限するために使用されるロックが関連付けられています。一度に1つのスレッドのみがこのロックを保持できます。ロックは再入可能です。1つのスレッドが特定の時間に複数回ロックを保持する場合があります。

3 (引用元:ISO/IEC 9899:201x 7.21.3 Streams 8)
ストリームの読み取り、書き込み、配置、または位置のクエリを行うすべての関数は、ストリームにアクセスする前にストリームをロックします。アクセスが完了すると、ストリームに関連付けられているロックが解除されます。再入可能:単一のスレッドが、特定の時間に複数回ロックを保持する場合があります。

4 (引用元:ISO/IEC 9899:201x 7.21.5.2フラッシュ機能2)
ストリームが、最新の操作が入力されていない出力ストリームまたは更新ストリームを指している場合、fflush関数により、そのストリームの未書き込みデータがホスト環境に配信され、ファイルに書き込まれます。それ以外の場合、動作は定義されていません。

39
2501

文字ストリーム(FILEタイプのオブジェクトへのポインターで表される)を操作するPOSIX.1およびC言語関数は、POSIX.1cによって必須再入可能性が達成されるように実装されます(を参照)。 ISO/IEC 9945:1-1996、§8.2)。

この要件には欠点があります。再入可能性のために関数の実装に組み込まれなければならない同期のために、かなりのパフォーマンスペナルティが課せられます。 POSIX.1cは、次のC言語標準I/O関数の高性能であるが再入可能ではない(潜在的に安全でない)バージョンを導入することにより、再入可能性(安全性)とパフォーマンスの間のこのトレードオフに対処します:getc()、getchar()、putc ()およびputchar()。再入可能でないバージョンには、安全性が低いことを強調するために、getc_unlocked()などの名前が付けられています。

ただし、他の人が指摘しているように、注意してください。多くの一般的なシステム(WindowsやAndroidを含む)はPOSIXに準拠していません。

12
geocar

入力ストリームでfflush()を呼び出すことは想定されておらず、未定義の動作が呼び出されるため、ストリームが書き込みモードまたは更新モードで開いていると想定します。

ストリームが更新モード(_"w+"_または_"r+"_)で開いている場合、fflush()を呼び出すときに最後の操作を読み取ってはなりません。ストリームはさまざまなスレッドで非同期に使用されるため、何らかの形式のプロセス間通信と、読み取りを行う場合の同期またはロックなしでこれを保証することは困難です。更新モードでファイルを開く正当な理由はまだありますが、fflushスレッドを開始した後に読み取りを行わないようにしてください。

fflush()は現在の位置を変更しません。バッファリングされた出力がシステムに書き込まれるだけです。ストリームは通常ロックによって保護されているため、あるスレッドでfflush()を呼び出しても、別のスレッドによって実行される出力が混乱することはありませんが、システム書き込みのタイミングが変わる可能性があります。複数のスレッドが同じ_FILE*_を出力する場合、インターリーブが発生する順序はとにかく不確定です。さらに、fseek()を使用する場合、同じストリームに対して異なるスレッドである場合は、必須独自のロックを使用して、fseek()と以下の間の一貫性を確保します。出力。

それは問題ないように見えますが、おそらくお勧めできません。代わりに、各スレッドでの書き込み操作の後、ロックを解放する前にfflush()を呼び出すことができます。

非常に簡単な答えですが、ファイルには単一の「現在の位置」があるため、そうしない場合があります。どのようにそれを追跡しますか?ファイルはシーケンシャルアクセス用に開かれていますか、それともランダムですか?後者の場合、複数回(スレッドごとに1つ)開くことができますが、ファイル構造の一貫性を保つ方法を考案することもできます。

実際の答えは、ストリーム自体がスレッドセーフであるということのようですが、そうでない場合は、別のスレッドでfflushが(ロックの外で)発生する可能性があります- is書き込み(クリティカルセクション内)。

そのため、コーディング対象の仮想マシンのモデルを使用して、fflush()もクリティカルセクション内に配置します。

0
Mark Hurd