Использование общей переменной 10 pthreads

проблема заключается в следующем:

Я хочу написать короткую программу, которая создает 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 %dn", 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() функция, но это не так помощь.

как реализовать синхронизацию потоков?

EDIT: ожидаемый результат не стоит 0...9 это может быть любая комбинация этих чисел, но каждое из них должно появиться только один раз.

5 ответов


есть две проблемы:

А. Вы используете lock но main не знает об этом замке.

B. A lock в этом случае недостаточно. Вы хотели бы, чтобы потоки сотрудничали, сигнализируя друг другу (потому что вы хотите main to не инкремент переменной до тех пор, пока поток не говорит, что это делается печать его). Вы можете использовать pthread_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;
}

Я вижу, что пример, который вы пытаетесь, - это игрушечная программа, чтобы, вероятно, узнать о библиотеке потоков POSIX. В реальном мире, как мы все знаем, это можно сделать намного быстрее, даже не используя потоки. Но ты уже знаешь это.


проблема заключается в следующем коде:

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

ваши параметры.значение id продолжает обновляться в основном потоке, в то время как вы передаете один и тот же указатель на все потоки.

пожалуйста, создайте отдельную память для параметров, динамически выделяя ее и передавая в разные потоки для решения проблемы.

EDIT1: Ваше использование мьютекса для защиты также является неправильной идеей. Хотя ваш мьютекс, если он используется в main при настройке id также, может сделать обновление взаимоисключающим,но вы не можете получить желаемый результат. Вместо получения значений от 0 .. 9 в разных потоках вы можете получить все 9 или несколько потоков могут печатать одинаковые значения.

таким образом, использование синхронизации потоков не такая хорошая идея для выходного, который вы ожидаете. Если вам все еще нужно использовать одну переменную param между всеми потоками и получить вывод от 0 до 9 из каждого потока, лучше переместить pthread_join в первый цикл. Этот гарантирует, что каждый поток создается, печатает значение и затем возвращается до того, как основной порождает следующий поток. В этом случае вам также не нужен мьютекс.

EDIT2: Что касается обновленного вопроса, то где спрашивается, что не нужно печатать цифры 0..9 в последовательности печать может быть случайной, но только один раз проблема все еще остается той же более или менее.

теперь, скажем, значение params.id первый 0 и поток 0 получил созданный, теперь, поток 0 должен распечатать его перед обновлением в основном потоке, иначе, когда поток 0 обращается к нему, значение params.id стал бы 1, и вы никогда не получите свой уникальный набор значений. Итак, как убедиться, что поток 0 печатает его, прежде чем он будет обновлен в main, двумя способами:

  • обеспечить поток 0 выполняет и печать до основного обновления значение
  • используйте переменные условия & сигнализировать для обеспечения что главный поток ждет для поток 0 для завершения печати перед обновлением значения (см. в ответ Арджуна ниже для более подробной информации)

по моему честному мнению, вы выбрали неправильную проблему для обучения синхронизации и общей памяти. Вы можете попробовать это с некоторыми хорошими проблемами, такими как" производитель-потребитель", где вам действительно нужна синхронизация для работы.


проблема в том, что вы изменяете параметры.id "незащищенный" в основном. Эта модификация в main также должна быть защищена мьютексом. Вы можете защитить этот доступ, локализовав его, создав функции getId() и setId (), которые будут блокировать мьютекс и защищать доступ к идентификатору следующим образом. Это, скорее всего, все равно даст сообщенную проблему, так как в зависимости от того, когда поток вызывает 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;
}

лучший способ получить уникальный идентификатор потока - определить метод 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));
}

самый простой способ получить желаемый результат - изменить ваш следующим образом:

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

EDIT: вот синхронизация для исправленного вопроса:

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

каждый поток должен иметь собственную копию идентификатора, чтобы другие потоки не изменяли идентификатор до его печати.


Я просто помещаю это здесь, чтобы предоставить другое решение этой проблемы - это не связано с мьютексами - нет синхронизации - нет условий и т. д. Основной dfference является то, что мы используем 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);  
}