Инициализируйте и деинициализируйте только один раз с несколькими потоками без мьютексов - PullRequest
0 голосов
/ 21 сентября 2018

У меня есть две функции initialize () и deinitialize (), и каждая функция должна запускаться только один раз.Структура примерно такая:

int *x;

initialize()
{
    x = malloc(sizeof(int) * 10);
}

deinitialize()
{
    free(x);
}

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

ОБНОВЛЕНИЕ: Извините за плохую информацию.Я на самом деле модифицирую библиотеку, которая содержит функции initialize () и deinitialize ().Мне нужно сделать эти две функции поточно-ориентированными.Пользователи могут использовать несколько потоков и могут вызывать эти функции более одного раза.Я не могу предположить, что пользователи будут вызывать ровно один раз для инициализации и деинициализации функций.Я могу только предположить, что если поток вызывает инициализацию, он будет вызывать deinitialize в какой-то момент.

Пользователи будут использовать библиотеку pthread для создания своих различных потоков.

1 Ответ

0 голосов
/ 21 сентября 2018

Я не знаю, чего вы хотите достичь, самое простое:

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

int *x;
int running;

void initialization(void)
{
    x = malloc(sizeof(int) * 10);
    puts("First thread init x");
}

void deinitialization(void)
{
    free(x);
    puts("Last thread reset x");
}


void *handler(void *data)
{
    printf("Thread started\n");
    while (running) {
      do_work();
    }
    printf("Thread exit\n");
}

int main(void)
{
    pthread_t threads[3];

    initialization();
    for (int i = 0; i < 3; i++)
        pthread_create(&threads[i], NULL, &handler, NULL);

    sleep(2);
    running = 0;
    for (int i = 0; i < 3; i++)
        pthread_join(threads[i], NULL);

    deinitialization();
    return 0;
}

Здесь вы можете быть уверены, что вы звонили init() и deinit() только один раз.

ОБНОВЛЕНИЕ

Другой вариант немного сложнее, но здесь вы также можете быть уверены, что init() вызывается только один раз.Поток с идентификатором 0 может начинаться после 1, в этом случае нам следует подождать, пока *x равно NULL.

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

#define MAX_THREADS 3

int *x;
int running;
int init;

struct thread_info
{
    pthread_t thread;
    int id;
    int first_id;
    int last_id;
};

void initialization(void)
{
    x = malloc(sizeof(int) * 10);
    puts("First thread init x");
    init = 1;
}

void deinitialization(void)
{
    free(x);
    puts("Last thread reset x");
}


void *handler(void *data)
{
    struct thread_info *tinfo = data;

    printf("Thread started\n");
    if (tinfo->id == 0)
        initialization();

    while (!init);
    /* EMPTY BODY */

    while (running) {
        do_work();
    }

    printf("Thread exit\n");
}

int main(void)
{
    struct thread_info threads[MAX_THREADS] =
    {
        [0 ... 2].id        = -1,
        [0 ... 2].first_id  = 0,
        [0 ... 2].last_id   = (MAX_THREADS - 1)
    };

    for (int i = 0; i < 3; i++)
        pthread_create(&threads[i].thread, NULL, &handler,
                      ((threads[i].id = i), &(threads[i])));

    sleep(2);
    running = 0;
    for (int i = 0; i < 3; i++)
        pthread_join(threads[i].thread, NULL);

    deinitialization();
    return 0;
}

deinit() немного сложно, потому что ваш последний поток может завершиться, и free(x), пока другой поток все еще работает и, возможно, использует его.Поэтому я оставляю его после завершения всех потоков.

В этом и заключается смысл параллельного программирования.Вы никогда не можете делать какие-либо предположения о порядке выполнения потоков.

Точное время выполнения задач в параллельной системе зависит от планирования, и задачи не всегда должны выполняться одновременно.Например, если заданы две задачи, T1 и T2:

  • T1 могут быть выполнены и завершены до T2 или наоборот (последовательный и последовательный)

  • T1 и T2 могут выполняться поочередно (последовательный и одновременный)

  • T1 и T2 могут выполняться одновременно в один и тот же момент времени (параллельный и одновременный)

...