没有互斥量的多个线程仅初始化和取消初始化一次

时间:2018-09-21 18:51:19

标签: c multithreading thread-safety pthreads

我有两个函数initialize()和deinitialize(),每个函数只能运行一次。结构类似于:

int *x;

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

deinitialize()
{
    free(x);
}

如何确保只有第一个线程调用初始化并且只有最后一个线程取消初始化。 不使用互斥锁就可以实现吗?

更新: 抱歉,信息不全。我实际上正在修改一个包含函数initialize()和deinitialize()的库。我需要使这两个函数成为线程安全的。用户可能使用多个线程,并且可能多次调用这些函数。我不能假设用户将只调用一次来初始化和取消初始化函数。我只能假设,如果线程调用了initialize,它将在某个时候调用deinitialize。

用户将使用pthread库创建他们不同的线程。

1 个答案:

答案 0 :(得分:0)

我不知道您要实现什么,最简单的是:

#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()仅调用一次。 ID为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可以在同一时刻同时执行 (并行和并发)