Linux 10 个 pthread 使用共享变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10879420/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 06:38:36  来源:igfitidea点击:

Using of shared variable by 10 pthreads

clinuxpthreadsmutex

提问by Alex

The problem is in following:

问题在于:

I want to write a short program that creates 10 threads and each prints a tread "id" that is passed to thread function by pointer.

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

Full code of the program is below:

该程序的完整代码如下:

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

The supposed output is (not necessary in this order):

假设的输出是(按此顺序不需要):

Hello from 0
....
Hello from 9

Actual result is:

实际结果是:

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

I tried to place mutex in different places in hello()function, but it didn't help.

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

How should I implement thread sync?

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

EDIT:Supposed result is not necessary 0...9 it can be any combination of these numbers, but each one should appear only one time.

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

采纳答案by ArjunShankar

There are two problems:

有两个问题:

A. You're using a lockbut mainis unaware of this lock.

A. 您正在使用lockmain不知道此锁。

B. A lockis not enough in this case. What you would want is for threads to cooperate by signalling each other (because you want mainto notincrement the variable until a thread says that it is done printing it). You can use a pthread_cond_tto achieve this (Look hereto learn more about this). This boils down to the following code (basically, I added an appropriate usage of pthread_cond_tto your code, and a bunch of comments explaining what is going on):

B.lock在这种情况下A是不够的。您想要的是线程通过相互发送信号来合作(因为您不想main在线程说它已完成打印之前增加变量)。您可以使用 apthread_cond_t来实现这一点(查看此处了解更多信息)。这归结为以下代码(基本上,我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;
}

I see that the example you are trying is a toy program to probably learn about the POSIX thread library. In the real world, as we all know this can be done much faster without even using threads. But you already know this.

我看到您正在尝试的示例是一个可能用于了解 POSIX 线程库的玩具程序。在现实世界中,众所周知,这甚至可以在不使用线程的情况下更快地完成。但是你已经知道了。

回答by Brady

The problem is that you are modifying the params.id "unprotected" in main. This modification in main also needs to be mutex protected. You could protect this access by localizing this by creating getId() and setId() functions that would lock the mutex and protect access to the id, as follows. This will most likely still give the problem reported, since depending on when the thread calls getData() it will have one value or another. So to solve this, you could add an incrementId() function and call it from the hello() function.

问题是您正在修改 main 中的 params.id “unprotected”。main 中的这个修改也需要互斥保护。您可以通过创建 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;
}

A better way to get a unique thread id would be to define the hello method as follows:

获得唯一线程 id 的更好方法是定义 hello 方法,如下所示:

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

And to avoid the problem with all threads trying to print at once, you could do the following:

为了避免所有线程尝试同时打印的问题,您可以执行以下操作:

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

回答by Jay

The problem lies in the below code:

问题在于下面的代码:

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

Your params.id value keeps getting updated in the main thread, whereas you are passing the same pointer to all the threads.

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

Please create seperate memory for params by dynamically allocating it and pass it to different threads to solve the problem.

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

EDIT1:Your usage of mutex to protect is also an incorrect idea. Though your mutex if used in main while setting the id also, may make the updation mutually exclusive, but you may not get your desired output. Instead of getting values from 0 .. 9 in different threads, you may get all 9s or still multiple threads may print same values.

EDIT1:您使用互斥锁来保护也是一个错误的想法。尽管您的互斥锁如果在设置 id 的同时在 main 中使用,可能会使更新互斥,但您可能无法获得所需的输出。不是在不同线程中从 0 .. 9 获取值,您可能会获取所有 9,或者多个线程仍可能打印相同的值。

So, using thread synchronization is not such a good idea for the output which you are expecting. If you still need to use one param variable between all threads and get output as 0 to 9 from each of the threads, better move the pthread_join into the first loop. This will ensure that each thread gets created, prints the value and then returns before the main spawns the next thread. In this case, you don't need the mutex also.

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

EDIT2:As for the updated question, where it is asked that it is not necessary to print the numbers 0..9 in a sequence, the printing can be random, but only once, the problem still remains the same more or less.

EDIT2:至于更新的问题,其中要求没有必要按顺序打印数字0..9,打印可以是随机的,但只有一次,问题仍然或多或少保持不变。

Now, let's say, the value of params.id is first 0 and thread 0 got created, now, thread 0 must print it before it is updated in the main thread, else, when thread 0 accessess it, the value of params.id would have become 1 and you will never get your unique set of values. So, how to ensure that thread 0 prints it before it is updated in main, Two ways for it:

现在,假设 params.id 的值首先是 0 并且线程 0 已创建,现在,线程 0 必须在主线程中更新它之前打印它,否则,当线程 0 访问它时,params.id 的值会变成 1 并且你永远不会得到你独特的值集。那么,如何确保线程 0 在 main 更新之前打印它,有两种方法:

  • Ensure thread 0 completes execution and printing before main updates the value
  • Use condition variables & signalling to ensure that main thread waits for thread 0 to complete printing before it updates the value (Refer to Arjun's answer below for more details)
  • 确保线程 0 在 main 更新值之前完成执行和打印
  • 使用条件变量和信号来确保主线程在更新值之前等待线程 0 完成打印(有关更多详细信息,请参阅下面的 Arjun 的回答)

In my honest opinion, you have selected the wrong problem for learning synchronization & shared memory. You can try this with some good problems like "Producer-Consumer", where you really need synchronization for things to work.

老实说,您为学习同步和共享内存选择了错误的问题。你可以用一些好的问题来尝试这个,比如“生产者-消费者”,在那里你真的需要同步才能工作。

回答by Alexandru C.

Easiest way to get the desired output would be to modify your mainfunction as follows:

获得所需输出的最简单方法是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;
}

Output would be:

输出将是:

Hello from 0
...
Hello from 9

EDIT: Here's the synchronization for the corrected question:

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

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

Each thread must have it's own copy of the id so that the other threads do not modify the id before it is printed.

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

回答by user2804865

I'm just putting this one here to provide another solution to this problem - this one does not involve mutexes - no synchronization - no conditionals, etc. The main dfference is that we are using pthread_detachto automatically release the thread's resources upon completion.

我只是把这个放在这里是为了为这个问题提供另一种解决方案——这个不涉及互斥锁——没有同步——没有条件等。主要的区别是我们正在使用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);  
}