简体   繁体   English

pthreads无法正常工作

[英]pthreads not working correctly

I have the following code which was simplified to show only the relevant part. 我将以下代码简化为仅显示相关部分。

My problem is that on one machine it shows correctly the thread number and all the other values but when I run it on other machines it shows the same values for all the threads created. 我的问题是在一台机器上它正确显示了线程号以及所有其他值,但是当我在其他机器上运行时,它为所有创建的线程显示了相同的值。

I compile it with -lpthread, I even tried to compile it statically but same results. 我使用-lpthread进行编译,甚至尝试静态编译但结果相同。

Why does it work correctly on one machine and on others not? 为什么在一台机器上不能正常工作? Is it some coding mistake or do I have to change libraries upon compilation? 是编码错误还是在编译时必须更改库? I am stuck. 我被困住了。 Thanks! 谢谢!

    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;
    }

Your have multiple issues in this code, starting with your arg structure. 从arg结构开始,此代码中存在多个问题。 The same logical arg structure is being shared by all your threads, introducing among other things, a severe race-condition: 您的所有线程都共享相同的逻辑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...
}

Now think for a moment what happens when that thread is launched. 现在考虑一下启动该线程时会发生什么。 if your for-loop launches multiple threads before some of them have a chance to access that arg-stuct and pull out their specific thread-info, then those threads will be accessing the last data your saved in it, which would be the last iteration of the loop (if you're really unlucky, you may catch it mid-update, but I'm not diving into that level of detail; the point should be obvious). 如果您的for循环在其中一些线程有机会访问该arg-stuct并提取其特定线程信息之前启动了多个线程,则这些线程将访问您保存在其中的最后一个数据,这将是最后一次迭代循环(如果您真的很不幸,您可能会在更新过程中发现它,但是我没有深入探讨细节;要点很明显)。

I suggest you dynamically allocate the thread arguments structure, and have the thread destroy it when finished. 我建议您动态分配线程参数结构,并让线程在完成时销毁它。 As a highly advised alternative (and commonly done) use that as your return value through pthread_join and then let main() destroy it after extracting the thread-finish data. 作为强烈建议的选择 (并且通常这样做) 请通过pthread_join用作返回值,然后在提取线程完成数据后让main()销毁它。

Secondly, your args struct is using a pointer for myword which is set to the same buffer for every thread (the char myword[20] local variable). 其次,您的args结构使用myword的指针,该指针为每个线程设置为相同的缓冲区char myword[20]局部变量)。 Therefore, even if you "fix" your argument structure to be dynamic you still have all your threads using the same buffer . 因此,即使您将参数结构“修复”为动态的,您仍将所有线程都使用相同的buffer

Solution

Dynamically allocate each threads argument structure. 动态分配每个线程的参数结构。 Within that, have a local copy of the word being processed. 在其中,包含要处理的单词的本地副本。 Likewise, store the return code for the thread the args will be passed to there as well (saves you the trouble of having to allocate one in the thread and free it in main() ). 同样,也将args传递给该线程的线程存储返回代码(省去了在线程中分配一个并在main()释放它的麻烦)。

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

In your thread startup loop: 在您的线程启动循环中:

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

And in the thread proc, simply do this: 在线程proc中,只需执行以下操作:

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;
}

Sorry for any typos I may have left, but I hope you get the point. 对不起,我可能会留下任何错别字,但我希望您能明白。


EDIT The OP requested a simple thread example that launches threads with custom argument blocks. 编辑 OP请求了一个简单的线程示例,该示例使用自定义参数块启动线程。 The following does just that as well as exposes the actual linked list directly to the thread crew. 这样做不仅可以将实际的链接列表直接显示给线程组。 The threads all share a common pointer (by address, so pointer to pointer) that initially points to the list head, and is protected with a mutex (which the threads also share). 所有线程都共享一个公共指针(按地址,因此是指向指针的指针),该指针最初指向列表头,并受到互斥锁(线程也共享)的保护。 All threads run until they detect the list is empty, at which point they exit. 所有线程运行,直到它们检测到列表为空,然后退出。 This means you could load a list significantly larger list than your pool (I chose a pool of 5, with a list of 20, but you can have many more entries in your list than that). 这意味着你可以加载列表比你的游泳池显著大名单(我选择了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;
}

Output (varies by system and run-instance) 输出 (因系统和运行实例而异)

(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

Note the thread id reporting each string. 请注意报告每个字符串的线程ID。 This proves each thread is consuming multiple entries on the list, but each is going to only one thread. 这证明了每个线程正在消耗列表中的多个条目,但是每个线程只会使用一个线程。 The shared pointer in the arguments block ensures this (as well as the obvious mutex protection). arguments块中的共享指针确保了这一点(以及明显的互斥保护)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM