【问题标题】:Pthread sharing variables with pointers in CPthread与C中的指针共享变量
【发布时间】:2016-04-13 18:50:26
【问题描述】:

我正在使用 Pthread 开发一个需要共享一些变量的 C 项目。已经写了几行代码,我刚刚意识到使用共享全局变量并不能很好地工作,因为缓存系统。

我在 stackoverflow 上发现一个解决方案是将变量的地址(在我的情况下不止一个)传递给线程函数,它有什么变化?

由于我的线程函数调用了其他将修改全局变量的函数,因此在一个函数修改全局变量的被调用函数链中添加一个参数有点痛苦。

所以我想知道,为每个全局变量声明全局指针并使用它们来访问全局变量而不是真正的全局变量是否有效?

我认为这是表面上的暗示,但为什么它最终不起作用?

我的程序是一个 UDP 网络协议,网络看起来像环或带圆圈的简单链表。网络上程序的实例称为实体。

一个实体实体可以插入一个环或要求一个实体创建另一个环(复制),因此另一个实体将位于两个环上。

接口有点像一个shell,命令可以导致在环上发送消息。消息在被看到后停止后在环上循环。

shell在主线程中,有一个线程处理消息,另一个管理插入,还有一个线程检测断环。问题位于环测试仪线程中。线程初始化一个实体的最大环数大小的全局数组(volatile short ring_check[NRING]),根据实际环数初始化第一个元素为0,其余为-1,然后发送测试消息每个响铃并在 30 秒超时期间休​​眠。超时结束后,它会检查数组中的值。

这些值由消息处理线程更改,当测试消息返回时,它通过其内容检测到它并将-1写入适当的ring_check元素。

问题是,在复制之后,两个环都被测试了,但是第二个环的检查失败了(ring_check[1] == 0),我真的不知道为什么......测试消息在发送后立即收到,在消息处理将ring_check[1] 修改为0 后,我打印它以查看是否确实进行了更改并打印1。但大约20 到30 秒后,ring_tester 从他的睡眠时间中醒来,它读取@987654329 @在ring_check[1]

short volatile ring_check[NRING+1];

// The function in the ring tester thread
static void test_ring() {
    // initialize ring_check array
    debug("ring_tester", GREEN "setting ring_check to -1...");
    char port_diff[5];
    // send test messages in each rings
    int fixed_nring = getnring();
    for (int i = fixed_nring+1; i < NRING; ++i) {
        ring_check[i] = -1;
    }
    for (int i = 0; i < fixed_nring + 1; i++) {
        debug("ring_tester", GREEN "setting ring_check %d to 0...", i);
        ring_check[i] = 0;
        itoa4(port_diff, ent.mdiff_port[i]);
        debug("ring_tester", GREEN "sending test to ring %d...", i);
        sendmessage(i, "TEST", "%s %s", ent.mdiff_ip[i], port_diff);
    }
    debug("test_ring", GREEN "timeout beginning...");
    sleep(timeout);
    debug("test_ring", GREEN "end of timeout.");

    for (int i = 0; i < fixed_nring + 1 && ring_check[i] != -1; i++) {
        debug("test_ring", GREEN "ring_check[%d]:%d", i, ring_check[i]);
        if (ring_check[i]) {
            debug("test_ring", GREEN "ring %d: checked.", i);
            continue;
        }
        else {
            debug("test_ring", GREEN "ring %d: checking failed. Ring broken...", i);
            continue;
        }
    }


 // The function called by the message treatment thread

static int action_test(char *message, char *content, int lookup_flag) {
    debug("action_test", RED "entering function...");
    if (content[15] != ' ' || content[20] != 0) {
        debug("action_test", RED "content not following the protocol."\
                "content: \"%s\"", content);
        return 1;
    }
    if (lookup_flag) {
        char mdiff_port[5];
        int fixed_nring = getnring();
        for (int i = 0; i < fixed_nring + 1 && ring_check[i] != -1; ++i) {
            itoa4(mdiff_port, ent.mdiff_port[i]);
            // find ring associated with message and actualize the checking
            if (strncmp(content, ent.mdiff_ip[i], 15) == 0 &&
                    strncmp(&content[16], mdiff_port, 4) == 0 &&
                    ring_check[i] != -1) {
                ring_check[i] = 1;
                debug("action_test", 
                        RED "correspondance found, ring_check[%d]:%d", i, ring_check[i]);
                return 0;
            }
        }
    }
    else {
        sendpacket_all(message);
    }
    return 0;
}

【问题讨论】:

  • “由于缓存系统,使用共享全局变量不能很好地工作”是什么意思,这对于指向共享对象的指针有何不同?
  • 我说的是当你不使用volatile 关键字时(stackoverflow.com/questions/78172/…),但当我这么说时我很困惑,因为变量被声明为volatile。问题出现在一个静态分配的数组volatile short ring_check[10] 上,该数组被一个线程修改,但另一个线程读取它并且看不到所做的更改。
  • *叹息*。 volatile 是一个红鲱鱼。相关的事情是 C11 草案标准 n1570,5.1.2.4 多线程执行和数据竞争,4 如果其中一个修改内存位置而另一个表达式评估冲突读取或修改相同的内存位置。 [。 ..] 25 如果程序的执行包含不同线程中的两个冲突操作,则程序的执行包含数据竞争,其中至少一个不是原子的,并且两者都不会在另一个之前发生。任何此类数据竞争都会导致未定义的行为。 这会影响 所有 共享对象,无论它们是否是全局的。
  • 写入和读取之间有 30 秒的延迟,因此在这种情况下不存在竞争。我编辑帖子汤姆使其更清晰......
  • @NicolasScottoDiPerto 如果这 30 秒以保证操作顺序的方式发生,那么你很好。但如果它只是“碰巧发生”,那么你就不好了。因为这是一件很难理解的事情,为什么不直接使用互斥体而不用担心呢?

标签: c pthreads


【解决方案1】:

您可以定义一个全局结构,例如 thread_inputparam。把所有全局变量的地址都放进去,发给所有线程,这个结构变量的地址。

int global1;
struct thread_input {
    int *g1;
   // add other globals'addresses
}thread_inputparam;
thread_inputparam.g1=&global1;

【讨论】:

  • 是的,这是我想到的第一个想法,但它会在我的代码中引起大量重构,因为线程创建后有很长的函数链,并且变量只能在结束时访问链,因此为链的每个函数添加参数并不优雅,它们与变量无关。线程可以访问全局变量,但它们对每个变量都有一个缓存副本,这会导致并发不一致。所以我想知道使用全局指针是否会欺骗该机制。
  • 我对缓存副本的理解不是很清楚。你能澄清一下吗?
  • 对不起,我很困惑,我想到了非volatile变量上可能出现的问题,但我的没有考虑。
  • @NicolasScottoDiPerto volatile 变量也会出现同样的问题。 volatile 关键字只是禁止编译器优化,但编译器并不是唯一可以优化内存访问的东西。
猜你喜欢
  • 2021-06-22
  • 1970-01-01
  • 2019-07-06
  • 2017-11-16
  • 1970-01-01
  • 2021-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多