1

関連する部分のみを表示するために簡略化された次のコードがあります。

私の問題は、あるマシンではスレッド番号と他のすべての値が正しく表示されますが、他のマシンで実行すると、作成されたすべてのスレッドで同じ値が表示されることです。

-lpthread でコンパイルしました。静的にコンパイルしようとしましたが、結果は同じでした。

あるマシンでは正しく動作し、他のマシンでは正しく動作しないのはなぜですか? コーディングの間違いですか、それともコンパイル時にライブラリを変更する必要がありますか? ハマった。ありがとう!

    pthread_mutex_t word_list;
    struct words_list {
        char myword[20];
        struct words_list * next;
    };
    struct arg_struct {
        char *myword;
        int t;
    };
    char myword[20];
    struct words_list * first_word = NULL;
            //the loading data into struct code is missing from here
    struct words_list * curr_word = first_word;
    pthread_mutex_init(&word_list,NULL);        
    int ex = 0;
    while(curr_word != NULL)
    {
        struct arg_struct args;
        int ret = -1;
        for(i = 0 ; i < max_thread; i++)
        {
            pthread_mutex_lock(&word_list);
            strncpy(myword,curr_word->myword,sizeof(myword) - 1);
            pthread_mutex_unlock(&word_list);
            args.myword = myword;
            args.t = i;

            //start threads
            if(pthread_create(&thread_id[i],NULL,&do_repl,&args) != 0)
            {
                i--;
                fprintf(stderr,RED "\nError in creating thread\n" NONE);
            }
            else
            {
                pthread_mutex_lock(&word_list);
                if(curr_word->next == NULL)
                    ex = 1;
                else
                    curr_word = curr_word->next;
                pthread_mutex_unlock(&word_list);
            }
        }//end threads creating

        for(i = 0 ; i < max_thread; i++)
        {
            void *join_result;
            if(pthread_join(thread_id[i],&join_result) != 0)
                fprintf(stderr,RED "\nError in joining thread\n" NONE);
            else
            {
                ret = *(int *)join_result;
                free(join_result);
                if((ret == 1)
                {
                    ex = 1;
                    break;
                }
            }
        }//end threads joining
        if (ex == 1)
            break;
    }//end while






    void* do_repl(void *arguments)
    {
        int *res = malloc(sizeof(int));
        struct arg_struct *args = arguments;
        char *word = args->word;
        int t = args->t;
        fprintf(stderr,"(%d) word: %s\n",t,word);
        *res = 0;
        return res;
    }
4

1 に答える 1

5

arg 構造から始めて、このコードには複数の問題があります。同じ論理引数構造がすべてのスレッドで共有されており、とりわけ深刻な競合状態が発生しています。

struct arg_struct args; // << == Note. Local variable.
int ret = -1;
for(i = 0 ; i < max_thread; i++)
{
    pthread_mutex_lock(&word_list);
    strncpy(myword,curr_word->myword,sizeof(myword) - 1);
    pthread_mutex_unlock(&word_list);
    args.myword = myword;
    args.t = i;

    //start threads NOTE: args passed by address.
    if(pthread_create(&thread_id[i],NULL,&do_repl,&args) != 0)
    {
        i--;
        fprintf(stderr,RED "\nError in creating thread\n" NONE);
    }

    // rest of code...
}

ここで、そのスレッドが起動されたときに何が起こるかを考えてみてください。forループが複数のスレッドを起動してから、それらのスレッドの一部がそのarg-stuctにアクセスして特定のスレッド情報を引き出す機会を得る場合、それらのスレッドは、保存された最後のデータにアクセスします。これが最後の反復になります(本当に運が悪い場合は、更新の途中で見つかるかもしれませんが、そのレベルの詳細には飛び込んでいません。要点は明らかです)。

スレッド引数構造体を動的に割り当て、終了時にスレッドに破棄させることをお勧めします。強くお勧めする(そして一般的に行われている) 代替手段として、それを戻り値として使用し、スレッド終了データを抽出した後に破棄pthread_joinさせます。main()

次に、args 構造体は、すべてのスレッド (ローカル変数)に対して同じバッファーmywordに設定されているポインターを使用しています。したがって、引数構造を動的に「修正」して、すべてのスレッドが同じ bufferを使用しています。char myword[20]

解決

各スレッドの引数構造体を動的に割り当てます。その中に、処理中の単語のローカルコピーがあります。同様に、引数が渡されるスレッドの戻りコードも保存します (スレッドに割り当てて で解放する手間を省きますmain())。

// thread arguments.
struct arg_struct
{
    char myword[20];
    int ret;
    int t;
};

スレッドの起動ループで:

while(curr_word != NULL)
{
    int ret = -1;
    for(i = 0 ; i < max_thread; i++)
    {
        // allocate a new  argument struct for the new thread
        struct arg_struct *args = calloc(1, sizeof(*args));
        args->t = i;

        // this lock is pointless, btw.
        pthread_mutex_lock(&word_list);
        strcpy(args->myword, cur_word->myword); //note: assumes no overrun.
        pthread_mutex_unlock(&word_list);

        //start threads
        if(pthread_create(&thread_id[i],NULL, &do_repl, args) != 0)
        {
            i--;
            fprintf(stderr,RED "\nError in creating thread\n" NONE);
        }
        else
        {
            pthread_mutex_lock(&word_list);
            if(curr_word->next == NULL)
                ex = 1;
            else
                curr_word = curr_word->next;
            pthread_mutex_unlock(&word_list);
        }
    }//end threads creating

    for(i = 0 ; i < max_thread; i++)
    {
        void *join_result = NULL;
        if(pthread_join(thread_id[i], &join_result) != 0)
            fprintf(stderr,RED "\nError in joining thread\n" NONE);
        else
        {
            ret = ((struct arg_struct*)join_result)->ret;
            free(join_result);
            if((ret == 1)
            {
                ex = 1;
                break;
            }
        }
    }//end threads joining

    if (ex == 1)
        break;
}//end while

スレッド プロシージャでは、次のようにします。

void* do_repl(void *arguments)
{
    struct arg_struct *args = arguments;
    fprintf(stderr,"(%d) word: %s\n", args->t, args->word);
    args->ret = 0;
    return args;
}

誤字脱字があるかもしれませんが、ご理解いただければ幸いです。


EDIT OPは、カスタム引数ブロックでスレッドを起動する簡単なスレッドの例を要求しました。次の例では、実際のリンク リストをスレッド クルーに直接公開するだけでなく、それを行っています。スレッドはすべて、最初にリストの先頭を指し、ミューテックス (スレッドも共有する) で保護されている共通のポインター (アドレスによる、つまりポインターへのポインター) を共有します。すべてのスレッドは、リストが空であることを検出するまで実行され、その時点で終了します。これは、プールよりもかなり大きなリストをロードできることを意味します (私は 5 のプールと 20 のリストを選択しましたが、リストにはそれよりも多くのエントリを含めることができます)。

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


typedef struct node
{
    char myword[20];
    struct node *next;
} node;

// thread arguments.
typedef struct arg_struct
{
    pthread_mutex_t *mtx;
    node **pp;
    int ret;
    int t;
} arg_struct;


// thread procedure. doesn't do much
void* do_repl(void *arguments)
{
    arg_struct *args = arguments;

    while (1)
    {
        // lock, get, and unlock
        pthread_mutex_lock(args->mtx);
        node *p = *args->pp;
        if (p)
        {
            *args->pp = p->next;
            pthread_mutex_unlock(args->mtx);

            // print the node we just got from the list.
            fprintf(stderr,"(%d) word: %s\n", args->t, p->myword);
        }
        else
        {
            // no more entries in list. break
            break;
        }
    };

    // make sure this is released
    pthread_mutex_unlock(args->mtx);

    args->ret = 0;
    return args;
}

// main entrypoint.
int main()
{
    // very simple. we use a fixed number of threads and list nodes.
    static const int n_threads = 5;

    // build a simple forward-only linked list. will have 4x the
    //  number of threads in our crew.
    node *list = NULL;
    node **next = &list;
    int i = 0;
    for (i=0;i<n_threads*4;++i)
    {
        node *p = malloc(sizeof(*p));
        sprintf(p->myword, "String-%d", i+1);
        *next = p;
        next = &(p->next);
    }
    *next = NULL;


    // declare a mutex and thread pool for hitting all the elements
    //  in the linked list.
    pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
    pthread_t threads[n_threads];

    // lock the mutex before creating the thread pool.
    pthread_mutex_lock(&mtx);

    i = 0;
    node *shared = list;
    for (int i=0; i<n_threads; ++i)
    {
        // setup some thread arguments.
        arg_struct *args = calloc(1, sizeof(*args));
        args->mtx = &mtx;
        args->pp = &shared;
        args->t = i+1;

        // launch the thread.
        pthread_create(threads + i, NULL, do_repl, args);
    }

    // now unlatch the mutex and wait for the threads to finish.
    pthread_mutex_unlock(&mtx);
    for (i=0;i<n_threads;++i)
    {
        void *pv = NULL;
        pthread_join(threads[i], &pv);

        arg_struct *args = pv;
        fprintf(stderr,"Thread %d finished\n", args->t);
        free(args);
    }

    // cleanup the linked list.
    while (list != NULL)
    {
        node *p = list;
        list = list->next;
        free(p);
    }

    return 0;
}

出力(システムおよび実行インスタンスによって異なります)

(2) word: String-2
(1) word: String-1
(3) word: String-3
(4) word: String-4
(5) word: String-5
(2) word: String-6
(1) word: String-7
(3) word: String-8
(4) word: String-9
(5) word: String-10
(2) word: String-11
(1) word: String-12
(3) word: String-13
(4) word: String-14
(2) word: String-16
(1) word: String-17
(5) word: String-15
(3) word: String-18
(4) word: String-19
(2) word: String-20
Thread 1 finished
Thread 2 finished
Thread 3 finished
Thread 4 finished
Thread 5 finished

各文字列を報告するスレッド ID に注意してください。これは、各スレッドがリストの複数のエントリを消費していることを証明していますが、それぞれは 1 つのスレッドにしか行きません。引数ブロックの共有ポインターは、これを保証します (明らかなミューテックス保護と同様に)。

于 2013-09-02T19:25:02.977 に答える