【问题标题】:Initialize and Deinitialize only once with multiple threads without mutexes使用多个没有互斥锁的线程只初始化和取消初始化一次
【发布时间】:2019-02-26 05:00:40
【问题描述】:

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

int *x;

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

deinitialize()
{
    free(x);
}

如何确保只有第一个线程调用初始化,只有最后一个线程调用 deinitialize。 这可以不使用互斥锁实现吗?

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

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

【问题讨论】:

  • 你是如何启动线程的?
  • 什么是“第一”,什么是“最后”?线程是否知道它们是“第一个”还是“最后一个”?
  • 您的意思是int *x; 吗?
  • '只有第一个线程调用初始化' - 在进程加载器启动的主线程中调用它,不要再调用它。 '只有最后一个线程调用 deinitialize' - 根本不要调用它。
  • @MartinJames 或者在所有线程加入后在主线程中调用deinitialize

标签: c multithreading thread-safety pthreads


【解决方案1】:

不知道你想达到什么,最简单的是:

#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 可以在同一时刻同时执行 (并行和并发)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-11
    • 2021-07-24
    • 2013-01-15
    • 1970-01-01
    • 2015-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多