5

次のプログラムを実行しています。すぐに死ぬスレッドを作成するだけです。

私が見つけたのは、93 から 98 (わずかに異なります) の呼び出しが成功した後、次の pthread_create() へのすべての呼び出しがエラー 11 で失敗することです: リソースが一時的に利用できません。スレッドを正しく閉じていると思うので、スレッドが持っているリソースを放棄する必要がありますが、一部のリソースが利用できなくなります。

プログラムの最初のパラメーターを使用すると、pthread_create() の呼び出し間の間隔を設定できますが、さまざまな値でテストしたところ、間隔は重要ではないことがわかりました (まあ、以前にエラーが発生します): 成功した回数通話は同じになります。

プログラムの 2 番目のパラメーターを使用すると、呼び出しが失敗した後にスリープ間隔を設定できますが、間隔の長さには違いがないようです。

ここで私はどの天井にぶつかっていますか?

編集: doSomething() でエラーが見つかりました: ロックをロック解除に変更すると、プログラムは正常に実行されます。疑問が残ります: エラーが修正されずに使い果たされているのはどのリソースですか?

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <pthread.h>
#include <errno.h>

pthread_mutex_t doSomethingLock;

void milliSleep(unsigned int milliSeconds)
{
    struct timespec ts;

    ts.tv_sec = floorf(((float)milliSeconds / 1000));
    ts.tv_nsec = ((((float)milliSeconds / 1000) - ts.tv_sec)) * 1000000000;
    nanosleep(&ts, NULL);
}

void *doSomething(void *args)
{
    pthread_detach(pthread_self());
    pthread_mutex_lock(&doSomethingLock);
    pthread_exit(NULL);
}


int main(int argc, char **argv)
{
    pthread_t doSomethingThread;
    pthread_mutexattr_t attr;
    int threadsCreated = 0;


    if (argc != 3)
    {
        fprintf(stderr, "usage: demo <interval between pthread_create() in ms> <time to wait after fail in ms>\n");
        exit(1);
    }

    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
    pthread_mutex_init(&doSomethingLock, &attr);

    while (1)
    {
        pthread_mutex_lock(&doSomethingLock);
        if (pthread_create(&doSomethingThread, NULL, doSomething, NULL) != 0)
        {
            fprintf(stderr, "%d pthread_create(): error %d, %m\n", threadsCreated, errno);
            milliSleep(atoi(argv[2]));
        }
        else threadsCreated++;
        milliSleep(atoi(argv[1]));
    }
}
4

2 に答える 2

5

32 ビット ディストリビューションを使用している場合は、おそらくアドレス空間の制限に達しています。最後に確認したところ、glibc は作成されたすべてのスレッドのスタック スペースに約 13MB を割り当てます (これはマッピングのサイズであり、割り当てられたメモリではありません)。98 個のスレッドを使用すると、利用可能な 3G のアドレス空間を 1 ギガバイトを超えることになります。

これをテストするには、エラーの後にプロセスをフリーズし (たとえばsleep(1000000)、何でも)、そのアドレス空間をpmap.

それが問題である場合は、 に渡す で小さいスタック サイズを設定してみpthread_attr_setstack()pthread_attr_tくださいpthread_create。明らかに、スタック要件を判断する必要がありますが、多くの場合、複雑なコードでさえ、わずか数キロバイトのスタックで正常に実行できます。

于 2012-10-11T16:57:01.400 に答える
2

あなたのプログラムは「単に消滅するスレッドを作成する」わけではありません。それはあなたが思っていることをしません。

まず、同じスレッドによってロックされているpthread_mutex_unlock()a のみをロック解除します。これがミューテックスの仕組みです。ミューテックスをロックしたのと同じスレッドによってのみロックを解除できます。セマフォ セマフォの動作が必要な場合は、セマフォ使用してください。pthread_mutex_t

doSomething()サンプルコードは、関数がロックしようとする再帰的ミューテックスを作成します。元のスレッドによって保持されているため、ブロックされます (呼び出しでミューテックスが解放されるのを待ちpthread_mutex_lock()ます)。doSomethingLock元のスレッドがロックを解放することはないため、 mutexの上に新しいスレッドを積み重ねるだけです。

ミューテックスに関する再帰性は、スレッドがそれを複数回ロックできることを意味します。ミューテックスが実際に解放されるには、同じ回数ロックを解除する必要があります。

pthread_mutex_lock()indoSomething()をに変更するpthread_mutex_unlock()と、そのスレッドが保持していないミューテックスのロックを解除しようとしています。呼び出しが失敗するとスレッドはすぐに終了します。


プログラムを修正したと仮定すると、(システムと使用可能な RAM に応じて) 100 ほどを超えるスレッドを作成できないことが次にわかります。

その理由は Andy Ross によってよく説明されています: 固定サイズのスタック (getrlimit(RLIMIT_STACK, (struct rlimit *)&info)スレッド属性を介して設定しない限り、スタックがどれだけ使用できるかがわかります) は、利用可能なアドレス空間を使い果たします。

プロセスに与えられた元のスタックは自動的にサイズ変更されますが、他のすべてのスレッドのスタック サイズは固定されています。デフォルトでは非常に大きいです。私のシステムでは、8388608 バイト (8 メガバイト) です。

私は個人的に非常に小さなスタック (通常は 65536 バイト) でスレッドを作成します。これは、関数がローカル配列や大きな構造体を使用したり、非常に深い再帰を実行したりしない限り、これで十分です。

#ifndef THREAD_STACK_SIZE
#define THREAD_STACK_SIZE  65536
#endif

pthread_attr_t   attrs;
pthread_t        thread[N];
int              i, result;

/* Create a thread attribute for the desired stack size. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE);

/* Create any number of threads.
 * The attributes are only a guide to pthread_create(),
 * they are not "consumed" by the call. */
for (i = 0; i < N; i++) {
    result = pthread_create(&thread[i], &attrs, some_func, (void *)i);
    if (result) {
        /* strerror(result) describes the error */
        break;
    }
}

/* You should destroy the attributes when you know
 * you won't be creating any further threads anymore. */
pthread_attr_destroy(&attrs);

最小スタック サイズは として利用可能でPTHREAD_STACK_MIN、 の倍数である必要がありsysconf(_SC_PAGESIZE)ます。現在PTHREAD_STACK_MIN == 16384は ですが、より大きな 2 の累乗を使用することをお勧めします。(どのバイナリ アーキテクチャでも、ページ サイズは常に2 のべき乗です。)

これは最小値にすぎず、pthread ライブラリは適切と思われるより大きな値を自由に使用できますが、実際には、スタック サイズは設定した値に加えて、アーキテクチャ、カーネル、および pthread に応じた固定値になるようです。ライブラリ版。コンパイル時定数の使用はほとんどすべての場合にうまく機能しますが、アプリケーションが構成ファイルを持つほど複雑な場合は、必要に応じてユーザーがコンパイル時定数をオーバーライドできるようにすることをお勧めします。ファイル。

于 2012-10-11T22:30:58.417 に答える