5

を使ってプログラムを書いていpthreadます。

環境: Windows 7、CYGWIN_NT-6.1 i686 Cygwin、gcc (GCC) 4.5.3

ソースコード

#include<stdio.h>
#include<pthread.h>

void *th_func(void *p)
{
    int iLoop = 0;

    for(iLoop = 0;iLoop<100;iLoop++)
    {
        printf("Thread Thread Thread Thread\n");
    }

    return;
}

int main()
{
    int iLoop = 0;
    pthread_t QueThread;

    printf("Main : Start Main\n");

    printf("Main : Start Create Thread\n");
    pthread_create(&QueThread,NULL,th_func,NULL);
    printf("Main : End Create Thread\n");

    for(iLoop = 0;iLoop<100;iLoop++)
    {
        printf("Main Main Main Main\n");
    }

    pthread_join(QueThread,NULL);

    printf("Main : End Main\n");

    printf("---------------\n");

    return 0;
}

ソース コードをコンパイルすると、警告やエラーは表示されませんが、出力がおかしいです。

その出力の一部

Main : Start Main
Main : Start Create Thread
Thread Thread Thread ThreThread Thread Thread Thread
Main Main Main Main
Thread Thread Thread Thread
Main Main Main Main

このような現象の原因が知りたいです。

この出力でMain : End Create Threadは、 は完全には印刷されません。そして 3 行目で、末尾の改行\n"Thread Thread Thread Thread\n"消えます。

みんなのアウトプットはこんな感じ?毎回発生するわけではありませんが、時々発生します。

安全に呼び出すためにミューテックスを使用するとprintf、奇妙な出力が停止したようです。

POSIXprintfはスレッドセーフであり、Cygwin.com によると、cygwin は posix スタイルの API を提供します。ただし、予期しない出力があります。

本当にprintfスレッドセーフですか?

Linux(Ubuntu)で同じプログラムを100回実行しましたが、この出力は出ませんでした。

さらに、出力の一部の単語が消えた理由がわかりません。

4

5 に答える 5

4

POSIX標準にはputc_unlocked()、解説にあるような機能があります。

スレッドセーフな方法で実装する必要がないことを除いて、元のバージョンと機能的に同等の関数getc()、、、、getchar()およびそれぞれの名前putc()putchar()付けられgetc_unlocked()たバージョンを提供する必要があります。これらは、 (または)およびで保護されたスコープ内でのみ安全に使用できます。これらの関数は、または関数の呼び出しが成功した後の場合のように、呼び出し元のスレッドが()オブジェクトを所有しているときに呼び出された場合にのみ、マルチスレッドプログラムで安全に使用できます。getchar_unlocked()putc_unlocked()putchar_unlocked()flockfile()ftrylockfile()funlockfile()FILE *flockfile()ftrylockfile()

これは、単一文字I/Oの低レベル関数が通常スレッドセーフであることを明確に示しています。ただし、粒度のレベルが1文字の出力操作であることも示しています。の仕様は次のprintf()とおりです。

fprintf()によって生成された文字は、呼び出さprintf()れたかのように印刷されます。fputc()

そしてputc()、それは言う:

このputc()関数は、マクロとして実装された場合にストリームを複数回評価する可能性があることを除いて、と同等でfputc()ある必要があります。したがって、引数は副作用のある式であってはなりません。

のページにfputc()はスレッドセーフについて何も書かれていないので、他の場所でその情報を探す必要があります。

別のセクションでは、スレッドについて説明し、次のように述べています。

POSIX.1-2008のこのボリュームで定義されているすべての関数は、次の関数がスレッドセーフである必要がないことを除いて、スレッドセーフである必要があります。

そして、以下のリストには*_unlocked()機能が含まれています。

したがって、スレッドセーフである必要がprintf()ありfputc()ますが、書き込みprintf()は「のように」行われるfputc()ため、スレッド間の出力のインターリーブは文字レベルで行われる可能性があります。これは、表示内容とほぼ一致しています。printf()非インターリーブを呼び出す場合は、との呼び出しを使用して、のflockfile()実行中にfunlockfile()スレッドの所有権を付与する必要があります。同様に。この結果を達成するために、非常に簡単に関数を書くことができます。stdoutprintf()fprintf()fprintf_locked()

int fprintf_locked(FILE *fp, const char *format, ...)
{
    flockfile(fp);
    va_list args;
    va_start(args, format);
    int rc = vfprintf(fp, format, args);
    va_end(args);
    funlockfile(fp);
    return rc;
}

fflush(fp)必要に応じて、そこにを挿入できます。vfprintf_locked()また、上記の関数を呼び出して、ロック、フォーマット、(フラッシュ)、およびロック解除の操作を実行することもできます。それが適切で実行可能であれば、コンパイラがコードをインライン化することを信頼して、おそらく私がそれをコーディングする方法です。を使用してバージョンをサポートすることstdoutも同様に非常に簡単です。

MichaelBurrが彼の回答flockfile()で引用したPOSIX仕様の断片に注意してください。

()オブジェクトを参照するすべての関数はFILE *、名前が。で終わる関数を除き、これらの( )オブジェクトの所有権を取得するために内部的に_unlocked使用するかのように動作する必要があります。flockfile()funlockfile()FILE *

の周りの奇妙な括弧は別としてFILE *、これらの行は他のすべての標準I / O機能に影響を与えますが、これらの行はあまり使用されないマニュアルページの1つに存在することを知っておく必要があります。したがって、私のfprintf_locked()機能は不要なはずです。それの異常な実装がfprintf()ファイルをロックしないfprintf_locked()ことがわかった場合は、代わりに関数を使用できますが、それは抗議の下でのみ実行する必要があります。ライブラリはとにかくそれを実行する必要があります。

于 2012-11-02T07:15:14.927 に答える
3

これは、Cygwin のバグであるか、何かが正しく構成されていないようです。ここでのいくつかの回答は、「スレッドセーフ」は関数がプログラムに害を及ぼさないことを約束するだけであり、スレッドセーフは必ずしも関数が「アトミック」であることを意味しないことを示しています。しかし、私の知る限り、POSIX は「スレッドセーフ」を正式に定義していません (そのような定義へのポインタがある場合は、コメントに投稿してください)。

printf()ただし、POSIXは がスレッドセーフであることを指定しているだけでなく、次のことも指定しています。

( FILE *) オブジェクトを参照するすべての関数は、内部で flockfile() および funlockfile() を使用してこれらの ( FILE *) オブジェクトの所有権を取得するかのように動作します。

オブジェクトをprintf()暗黙的に参照するため、すべての呼び出しは相互に (および を使用する他のすべての関数に対して) アトミックである必要があります。stdout FILE*printf()stdout

これは他のシステムには当てはまらないかもしれませんが、私の経験では、多くのマルチスレッド システムに当てはまります。

于 2012-11-02T07:01:07.817 に答える
0

関数がスレッドセーフであるからといって、それがアトミックであるとは限りません。

あなたの場合、出力がインターリーブされないようにしたい場合は、ミューテックスを使用して、一度に 1 つのスレッドのみが呼び出されるようにする必要がありますprintf

于 2012-11-02T06:21:22.897 に答える
0

スレッドがこのように動作するのには理由があります。スレッドが「同時に」ではなく (インターリーブ方式で) 次々に実行される場合、この種の「並行性」には意味がありません。ミューテックスを使用すると、意図に応じてスレッドがブロックされ、期待される出力が生成されます。

また、return;返す関数を記述しますがvoid *、これは未定義の動作であるため、プログラムの実行時に何かが発生する可能性があります。

于 2012-11-02T06:31:24.967 に答える
0

これを、リソースにアクセスしようとしている 2 つのスレッドがある簡単な方法で説明します。また、優先度チェックやミューテックスのようなものはありません。理論的には、ミューテックスや優先順位のないスレッドにはリソースがランダムに割り当てられます。1 つのスレッドが yes を印刷し、もう 1 つのスレッドが no を印刷する 2 つのスレッドを作成してみてください。この異常な動作が見つかります。また、この場合、実行時間はスレッドごとに異なることに注意してください。あるスレッドが情報をファイルに書き込み、他のスレッドがコンソールに書き込むことで同じことを試してみると. このような問題は発生しません。それが役立つことを願って....

于 2012-11-02T06:49:18.220 に答える