通过10个pthreads使用共享变量

时间:2012-06-04 09:54:18

标签: c linux pthreads mutex

问题在于:

我想编写一个创建10个线程的短程序,每个程序都打印一个通过指针传递给线程函数的“id”。

该计划的完整代码如下:

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

struct params {
        pthread_mutex_t mutex;
        int id;
};

typedef struct params params_t;

void* hello(void* arg){
    int id;
    pthread_mutex_lock(&(*(params_t*)(arg)).mutex);
    id = (*(params_t*)(arg)).id;
    pthread_mutex_unlock(&(*(params_t*)(arg)).mutex);
    printf("Hello from %d\n", id);
}


int main() {
    pthread_t threads[10];
    params_t params;
    pthread_mutex_init (&params.mutex , NULL);

    int i;
    for(i = 0; i < 10; i++) {
            params.id = i;
            if(pthread_create(&threads[i], NULL, hello, &params));
    }

    for(i = 0; i < 10; i++) {
            pthread_join(threads[i], NULL);
    }

    return 0;
}

假定的输出是(按此顺序不必要):

Hello from 0
....
Hello from 9

实际结果是:

Hello from 2
Hello from 3
Hello from 3
Hello from 4
Hello from 5
Hello from 6
Hello from 8
Hello from 9
Hello from 9
Hello from 9

我尝试将互斥锁放在hello()函数的不同位置,但它没有帮助。

我应该如何实现线程同步?

编辑:假定的结果不是必需的0 ... 9它可以是这些数字的任意组合,但每个数字只能出现一次。

5 个答案:

答案 0 :(得分:7)

问题在于以下代码:

for(i = 0; i < 10; i++) 
{             
  params.id = i;             
 if(pthread_create(&threads[i], NULL, hello, &params));     
} 

你的params.id值在主线程中不断更新,而你将相同的指针传递给所有线程。

请通过动态分配params为params创建单独的内存并将其传递给不同的线程来解决问题。

<强> EDIT1: 您使用互斥锁进行保护也是一个错误的想法。虽然你的互斥锁如果在主设备中使用时也可以设置id,可能会使更新互斥,但你可能无法获得所需的输出。不是在不同的线程中从0 ... 9获取值,而是可以获得所有9个或多个线程可以打印相同的值。

因此,对于您期望的输出,使用线程同步并不是一个好主意。如果你仍然需要在所有线程之间使用一个param变量并从每个线程获得0到9的输出,那么最好将pthread_join移动到第一个循环中。这将确保每个线程都被创建,打印该值,然后在main生成下一个线程之前返回。在这种情况下,您也不需要互斥锁。

<强> EDIT2: 至于更新的问题,在被问及没有必要按序列打印数字0..9的情况下,打印可以是随机的,但只有一次,问题仍然或多或少地保持不变。

现在,让我们说,params.id的值是0,线程0已经创建,现在,线程0必须在主线程更新之前打印它,否则,当线程0访问它时,值为0 params.id将成为1,你永远不会得到你独特的价值观。那么,如何确保线程0在main更新之前打印它,有两种方式:

  • 确保线程0在主要更新之前完成执行和打印 价值
  • 使用条件变量&amp;发信号以确保主线程等待 线程0在更新值之前完成打印(参见 以下是Arjun的回答以获取更多详情)

老实说,你选择了错误的学习同步问题。共享内存。你可以尝试一些像“生产者 - 消费者”这样的好问题,你真的需要同步工作。

答案 1 :(得分:2)

有两个问题:

一个。您使用的是lock,但main并未发现此锁定。

B中。在这种情况下,lock是不够的。你想要的是让线程通过相互通信来协作(因为你希望main 增加变量,直到线程说它完成打印它为止。您可以使用pthread_cond_t来实现此目的(Look here以了解有关此内容的更多信息)。这归结为以下代码(基本上,我在您的代码中添加了pthread_cond_t的适当用法,以及一堆解释正在发生的事情的评论):

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

struct params {
        pthread_mutex_t mutex;
        pthread_cond_t done;
        int id;
};

typedef struct params params_t;

void* hello(void* arg){

    int id;
    /* Lock.  */
    pthread_mutex_lock(&(*(params_t*)(arg)).mutex);

    /* Work.  */
    id = (*(params_t*)(arg)).id;
    printf("Hello from %d\n", id);

    /* Unlock and signal completion.  */
    pthread_mutex_unlock(&(*(params_t*)(arg)).mutex);
    pthread_cond_signal (&(*(params_t*)(arg)).done);

    /* After signalling `main`, the thread could actually
    go on to do more work in parallel.  */
}


int main() {

    pthread_t threads[10];
    params_t params;
    pthread_mutex_init (&params.mutex , NULL);
    pthread_cond_init (&params.done, NULL);

    /* Obtain a lock on the parameter.  */
    pthread_mutex_lock (&params.mutex);

    int i;
    for(i = 0; i < 10; i++) {

            /* Change the parameter (I own it).  */    
            params.id = i;

            /* Spawn a thread.  */
            pthread_create(&threads[i], NULL, hello, &params);

            /* Give up the lock, wait till thread is 'done',
            then reacquire the lock.  */
            pthread_cond_wait (&params.done, &params.mutex);
    }

    for(i = 0; i < 10; i++) {
            pthread_join(threads[i], NULL);
    }

    /* Destroy all synchronization primitives.  */    
    pthread_mutex_destroy (&params.mutex);
    pthread_cond_destroy (&params.done);

    return 0;
}

我看到您正在尝试的示例是一个可能了解POSIX线程库的玩具程序。在现实世界中,我们都知道,即​​使不使用线程,也可以更快地完成。但你已经知道了。

答案 2 :(得分:1)

问题是你正在修改主要的params.id“unprotected”。主要的修改也需要互斥保护。您可以通过创建getId()和setId()函数来本地化来保护此访问,这些函数将锁定互斥锁并保护对id的访问,如下所示。这很可能仍然会报告问题,因为根据线程调用getData()的时间,它将有一个或另一个值。所以要解决这个问题,你可以添加一个incrementId()函数并从hello()函数中调用它。

struct params {
        pthread_mutex_t mutex;
        int id;
};

typedef struct params params_t;

int getId(params_t *p)
{
    int id;
    pthread_mutex_lock(&(p->mutex));
    id = p->id;
    pthread_mutex_unlock(&(p->mutex));

    return id;

}

void setId(params_t *p, int val)
{
    pthread_mutex_lock(&(p->mutex));
    p->id = val;
    pthread_mutex_unlock(&(p->mutex));
}

void incrementId(params_t *p)
{
    pthread_mutex_lock(&(p->mutex));
    p->id++;
    pthread_mutex_unlock(&(p->mutex));
}

void* hello(void* arg){
    params_t *p = (params_t*)(arg);
    incrementId(p);
    int id = getId(p);

    // This could possibly be quite messy since it
    // could print the data for multiple threads at once
    printf("Hello from %d\n", id);
}


int main() {
    pthread_t threads[10];
    params_t params;
    params.id = 0;
    pthread_mutex_init (&params.mutex , NULL);

    int i;
    for(i = 0; i < 10; i++) {
            if(pthread_create(&threads[i], NULL, hello, &params));
    }

    for(i = 0; i < 10; i++) {
            pthread_join(threads[i], NULL);
    }

    return 0;
}

获取唯一线程ID的更好方法是按如下方式定义hello方法:

void* hello(void* arg){
    pthread_t threadId = pthread_self();
    printf("Hello from %d\n", threadId);
}

为避免所有线程一次尝试打印出现问题,您可以执行以下操作:

void* hello(void* arg){
    params_t *p = (params_t*)(arg);
    pthread_mutex_lock(&(p->mutex));

    p->id++;
    int id = p->id;
    printf("Hello from %d\n", id);

    pthread_mutex_unlock(&(p->mutex));
}

答案 3 :(得分:0)

获得所需输出的最简单方法是修改main函数,如下所示:

int main() {
    pthread_t threads[10];
    params_t params;
    pthread_mutex_init (&params.mutex , NULL);

    int i;
    for(i = 0; i < 10; i++) {
            params.id = i;
            if(pthread_create(&threads[i], NULL, hello, &params));
            pthread_join(threads[i], NULL); //wait for thread to finish
    }

    /*for(i = 0; i < 10; i++) {
            pthread_join(threads[i], NULL);
    }*/

    return 0;
}

输出将是:

Hello from 0
...
Hello from 9

编辑:这是纠正问题的同步:

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

struct params {
    pthread_mutex_t* mutex;
    int id;
};

typedef struct params params_t;

void* hello(void* arg){
    int id = 0;
    params_t* params = (params_t*)arg;
    if(params != 0)
    {
        id = params->id;
        delete params;
        params = 0;
    }
    printf("Hello from %d\n", id);
}


int main() {
    pthread_t threads[10];
    params_t* params = 0;
    pthread_mutex_t main_mutex;
    pthread_mutex_init (&main_mutex , NULL);

    int i;
    for(i = 0; i < 10; i++) {
        params = new params_t(); //create copy of the id to pass to each thread -> each thread will have it's own copy of the id
        params->id = i;
        params->mutex = &main_mutex;
        if(pthread_create(&threads[i], NULL, hello, params));
    }

    for(i = 0; i < 10; i++) {
        pthread_join(threads[i], NULL);
    }

    return 0;
}

每个线程必须拥有自己的id副本,以便其他线程在打印之前不会修改id。

答案 4 :(得分:0)

我只是把这个放在这里为这个问题提供另一种解决方案 - 这个问题不涉及互斥 - 没有同步 - 没有条件等。 主要的不同之处在于我们使用pthread_detach在完成后自动释放线程的资源。

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

#define NUMTHREADS 10        

typedef struct params {    
    int id;     
} params_t;                                                                     

void* hello(void *arg)  
{ 
    params_t *p = (params_t*)arg;     
    int status; 
    status = pthread_detach(pthread_self());      
    if (status !=0 )     
    {       
        printf("detaching thread\n");     
        abort();      
     }                                                                           

     printf("Hello from %d\n", p->id);   
     free(p);    
     return NULL;  
 }                     

int main()   
{ 
    pthread_t thread;    
    params_t *par;     
    int i, status;                                                              
    for (i=0; i<NUMTHREADS; i++)    
    {     
        par = (params_t*)malloc(sizeof(params_t));       
        if (par == NULL)    
        {       
            printf("allocating params_t");   
            abort();     
         }                                                                       

        par->id = i;  
        status = pthread_create(&thread, NULL, hello, par);   
        if (status != 0)    
            exit(1);     
    }      
    /* DO some more work ...*/                                                       
    sleep(3);                                                                         
    exit(0);  
}