【问题标题】:Make a variable that is initialized by a function available to a function in multithreaded environment使由函数初始化的变量可用于多线程环境中的函数
【发布时间】:2017-01-01 05:13:44
【问题描述】:

所以这是我要解决的问题,我正在用 C 编程。

我们有一个函数可以为你初始化一个结构。

typedef struct {
  int val1;
  int val2;
} custom_t;

custom_t init_custom() {
  custom_t temp;

  temp.val1 = 5;
  temp.val2 = 5;


  return temp;
}

你会像这样使用它:

custom_t some_name = init_custom();

我有 4 个以 custom_t 作为输入的函数,可以用它做一些工作。

在另一个文件中,我有很多将在多线程环境中运行的库函数。无论线程如何,这些库函数都需要在同一个 custom_t 变量上工作。

库函数不会将 custom_t 变量传递给它,因为目标是其他用户应该能够使用库函数而无需考虑 custom_t 变量。

我在想我必须在定义库函数的命名空间中将 custom_t 变量设为全局变量,但我错误地说全局变量必须是 const。

我不确定如何实现这一点,我将不胜感激能获得的所有帮助。如果我的解释不够好,请随时提出任何问题,我会尽力详细说明。

编辑: 修正变量初始化错字

【问题讨论】:

  • C 不要求具有外部链接的变量为const,因此无论您遇到什么错误,都必须针对特定的用法或上下文。如果您需要这方面的帮助,请提交 minimal reproducible example
  • 另一方面,我们确实希望在这里提出具体问题。 “我该如何处理?”很少有资格。
  • 您是否有可能调用将init_custom 分配给标题中的全局变量?否则:如果你有一个不是线程安全的库,并且你试图通过在你交给它的数据结构上实现线程安全来做到这一点,那么,通常事情不会很好地结束。您可以使访问足够安全,以至于没有人在其他人正在阅读的同时写入,但谁能说代码对访问结构不同部分的一致性和原子性的假设是什么?

标签: c multithreading shared-resource


【解决方案1】:

使用custom_t = init_custom();,您正在尝试设置一个类型名(即custom_t)。

就叫别的吧:

custom_t my_global_custom = init_custom();

但是,要从多个线程和库函数中访问它,假设您需要写入它,您需要将对此的访问包装在互斥体中:

pthread_mutex_t custom_mutex = PTHREAD_MUTEX_INITIALIZER;
custom_t my_global_custom;

my_global_custom = init_custom();

// how each thread must access it
pthread_mutex_lock(&custom_mutex);
func_that_uses_my_global_custom();
pthread_mutex_unlock(&custom_mutex);

更新:

我的示例不是字面意思初始化器,而是赋值

pthread_mutex_t custom_mutex = PTHREAD_MUTEX_INITIALIZER;
custom_t my_global_custom;
custom_t my_global_2;

custom_t
init_custom(void)
{
    custom_t temp;

    temp.val1 = 5;
    temp.val2 = 5;

    return temp;
}

void
init_custom2(custom_t *temp)
{

    temp->val1 = 5;
    temp->val2 = 5;
}

int
main(void)
{

    // either one of these should work ..
    my_global_custom = init_custom();
    init_custom2(&my_global_2);

    // start some threads ...

    return 0;
}

void *
thread_func(void *)
{

    // how each thread must access it:
    pthread_mutex_lock(&custom_mutex);
    func_that_uses_my_global_custom();
    pthread_mutex_unlock(&custom_mutex);

    return (void *) 0;
}

更新 #2:

但是您知道在主函数之外初始化 my_global_custom 的任何方法吗?或者那是不可能的?

另一种方法[至少在gcc 下],是创建一个 contructor 函数。鉴于上述函数和定义,将 init 调用移至:

void __attribute__((constructor))
my_global_constructor(void)
{

    my_global_custom = init_custom();
    init_custom2(&my_global_2);
}

没有什么需要[也没有应该]调用这个函数。它会在之前自动调用main,因为它现在是一个特殊函数。

这些经常被想要做一些初始化的库使用,但不想想要负担main不得不知道 调用(例如)init_liba(); init_libb(); ... 在这种情况下,它在库的“正确”时间调用[基于链接等]。

还有一个__attribute__((destructor)) 可以用来“破坏”东西[main 返回后,IIRC]。

有关更多信息,请参阅:How exactly does __attribute__((constructor)) work?

就个人而言,我现在使用上述属性,但出于怀旧,我喜欢较旧的.init/.fini 部分。

【讨论】:

  • 如果my_global_custom 是在文件范围内声明的,如果它要具有外部链接就必须如此,那么它的初始值设定项必须是编译时常量。它不能通过函数调用来初始化。
  • 嘿,custom_t = init_custom();是一个错字,它应该像你说的那样使用。并且该变量受到互斥锁的保护。我的问题是我无法使用函数创建 my_global_custom,因为它给出的错误是全局变量必须是 const
  • @JohnBollinger 这正是我的问题。那我该怎么办呢?
【解决方案2】:

正如您在@CraigEstey 的回答中所阐明的,问题不在于变量必须是const,而是它的initializer 需要是编译时常量。解决此类问题的一种方法是通过宏定义静态初始化值,而不是作为函数返回值。比如在一些合适的头文件中声明

typedef struct {
    int val1;
    int val2;
} custom_t;

#define CUSTOM_T_INITIALIZER { .val1 = 5, .val2 = 5 }

然后您可以像这样初始化custom_t 类型的变量:

custom_t some_name = CUSTOM_T_INITIALIZER;

,包括在文件范围内。

更新:

如果您需要为文件范围或静态持续时间变量分配一个不是编译时常量的值(包括如果它具有聚合类型并且其成员之一所需的值不是编译时常量),那么您不能为此目的使用初始化程序。相反,您必须安排在程序启动后将所需的值分配给该变量,可能是通过调用某种初始化函数。

【讨论】:

  • 这是一个很酷的技巧,不知道。谢谢你。 val1需要用函数初始化怎么办?
  • @Coolmatt69,如果您希望任何具有静态存储持续时间的对象(包括使用文件范围声明的任何变量)获取一个不是编译时常量的值,那么您必须将该值分配给它作为程序正常执行的一部分。您不能为此目的使用初始化程序。
猜你喜欢
  • 1970-01-01
  • 2013-05-20
  • 2021-05-22
  • 2021-07-08
  • 2022-11-25
  • 2015-06-11
  • 1970-01-01
  • 1970-01-01
  • 2016-09-23
相关资源
最近更新 更多