Могут ли pthreads делиться только глобальными ресурсами? - PullRequest
4 голосов
/ 14 сентября 2009

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

Я обнаружил, что когда я изменяю значение члена этой структуры, это изменение не отражается когда-либо в другой pthread Есть ли способ разделить неглобальную переменную (например, целое число) между двумя потоками, чтобы изменения, внесенные в эту переменную в одном потоке, появились в другом?

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

Ответы [ 4 ]

8 голосов
/ 14 сентября 2009

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

void foo(void)
{
    pthread_t t1, t2;
    struct foo common_value;

    if (pthread_create(&t1, NULL, a, &common_value) != 0)
    {
        return -1;
    }    

    if (pthread_create(&t2, NULL, b, &common_value) != 0)
    {
        return -1;
    }    

    // upon exiting this function, common_value is no longer what you think it is!
}
2 голосов
/ 14 сентября 2009

Я не претендую на элегантность, но мне кажется, это работает на MacOS X 10.5.8.

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

static void *writer(void *arg)
{
    int * volatile i = arg;

    for (*i = 1; *i < 10; (*i)++)
    {
        printf("writer(): pseudo_global = %d\n", *i);
        fflush(stdout);
        sleep(1);
    }
    printf("writer(): pseudo_global = %d (exiting)\n", *i);
    fflush(stdout);
    return(0);
}

static void *reader(void *arg)
{
    int * volatile i = arg;
    while (*i < 10)
    {
        printf("reader(): pseudo_global = %d\n", *i);
        fflush(stdout);
        sleep(1);
    }
    printf("reader(): pseudo_global = %d (exiting)\n", *i);
    fflush(stdout);
    exit(0);
}

int main(void)
{
    volatile int pseudo_global = 0;
    pthread_t t1;
    pthread_t t2;
    if (pthread_create(&t1, 0, writer, &pseudo_global) != 0)
    {
        perror("pthread_create() for thread 1");
        exit(1);
    }
    if (pthread_create(&t2, 0, reader, &pseudo_global) != 0)
    {
        perror("pthread_create() for thread 1");
        exit(1);
    }
    while (pseudo_global < 10)
    {
        printf("main():   pseudo_global = %d\n", pseudo_global);
        fflush(stdout);
        sleep(1);
    }
    printf("main():   pseudo_global = %d (exiting)\n", pseudo_global);
    fflush(stdout);
    return(0);
}

Обратите внимание, что я добавил в код квалификатор 'volatile', чтобы 'удостовериться', но кроме получения предупреждений об исключенных квалификаторах в вызовах pthread_create(), это не имело существенного значения. Я также без проблем запустил код без volatile квалификаторов.

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

Я готов поверить, что должен быть более осторожным с завершением потока и должен убедиться, что main() не завершится без использования pthread_join(), чтобы убедиться, что потоки вышли первыми.

Пример вывода:

$ make ptex
gcc -O ptex.c -o ptex  
ptex.c: In function ‘main’:
ptex.c:40: warning: passing argument 4 of ‘pthread_create’ discards qualifiers from pointer target type
ptex.c:45: warning: passing argument 4 of ‘pthread_create’ discards qualifiers from pointer target type
$ ./ptex
writer(): pseudo_global = 1
main():   pseudo_global = 0
reader(): pseudo_global = 1
writer(): pseudo_global = 2
main():   pseudo_global = 2
reader(): pseudo_global = 2
writer(): pseudo_global = 3
main():   pseudo_global = 3
reader(): pseudo_global = 3
writer(): pseudo_global = 4
main():   pseudo_global = 4
reader(): pseudo_global = 4
writer(): pseudo_global = 5
reader(): pseudo_global = 5
main():   pseudo_global = 5
writer(): pseudo_global = 6
reader(): pseudo_global = 6
main():   pseudo_global = 6
writer(): pseudo_global = 7
reader(): pseudo_global = 7
main():   pseudo_global = 7
writer(): pseudo_global = 8
reader(): pseudo_global = 8
main():   pseudo_global = 8
writer(): pseudo_global = 9
reader(): pseudo_global = 9
main():   pseudo_global = 9
writer(): pseudo_global = 10 (exiting)
reader(): pseudo_global = 10 (exiting)
main():   pseudo_global = 10 (exiting)
$
1 голос
/ 19 сентября 2009

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

Сказав это, я думаю, что использование глобального - на самом деле лучшее решение. Поместите глобальный объект в отдельный файл и объявите его статический , чтобы он был глобальным для файла. Затем, только поместите ваши функции потока в этот файл, чтобы только они имели доступ к переменной. Мне это нравится больше, так как это делает очевидным, что память распределяется между потоками. Аргумент pthread_create на самом деле предназначен для контекста, специфичного для потока, и из анализа функций потока не обязательно будет ясно, что всем потокам передается один и тот же указатель.

1 голос
/ 14 сентября 2009

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

После того, как вы проверили это, пожалуйста, опубликуйте простой пример кода, иллюстрирующий вашу проблему, который могут проверить другие.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...