使用全局变量

时间:2018-05-08 14:31:55

标签: c linux multithreading pthreads

我正在研究Linux和操作系统中使用的Thread。我正在做一些运动。目标是将一个全局变量的值相加,最后查看结果。当我看到最后的结果时,我的思绪就是打击。代码是以下一个

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

int i = 5;

void *sum(int *info);

void *sum(int *info)
{
    //int *calc = info (what happened?)
    int calc = info;

    i = i + calc;

    return NULL;
}

int main()
{
    int rc = 0,status;
    int x = 5;

    pthread_t thread;

    pthread_t tid;
    pthread_attr_t attr;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    rc = pthread_create(&thread, &attr, &sum, (void*)x);
    if (rc) 
    {              
        printf("ERROR; return code from pthread_create() is %d\n", rc);
        exit(-1);
    }

    rc = pthread_join(thread, (void **) &status);
    if (rc) 
    {
        printf("ERROR; return code from pthread_join() is %d\n", rc);
        exit(-1);
    }

    printf("FINAL:\nValue of i = %d\n",i);
    pthread_attr_destroy(&attr);
    pthread_exit(NULL);

    return 0;
}

如果我将sum函数中的变量calc作为int * cal,则i的最终值为25(不是预期值)。但是如果我把它作为int calc,那么i的最终值是10(我在这个练习中的期望值)。当我把变量calc作为int * calc。

时,我不明白i的值怎么可能是25

2 个答案:

答案 0 :(得分:2)

阅读一些tutorial about pthreads。在多个threads中访问和修改全局数据时,您无法期望可重现的行为(没有与synchronization相关的其他编码预防措施)。 AFAIU你的代码展示了一些棘手的undefined behavior,你应该是scared(也许在你的情况下它只是未指明的行为)。要解释所观察到的具体行为,您需要深入了解实现细节(并且您没有时间:研究生成的汇编代码,特定硬件的行为等等。)

另外(因为info指针int

int calc = info;

没有多大意义(我猜你做了一些错字)。在某些系统上(比如运行Linux的x86-64),指针比int宽(因此calc会丢失info的一半位。在其他(稀有)系统上,它的尺寸可能更小。 Somtimes(运行Linux的i686)可能具有相同的大小。如果要将指针转换为整数值并返回,则应考虑<stdint.h>中的intptr_t

实际上,您应该使用mutex保护对全局数据的访问(在i内,可能通过指针访问),或者在C11 atomic操作中使用,因为该数据是由多个并发线程使用。

所以你可以声明像

这样的全局互斥
 pthread_mutext_t mtx = PTHREAD_MUTEX_INITIALIZER;

(或使用pthread_mutex_init)然后在sum中编码

pthread_mutex_lock(&mtx);
i = i + calc;
pthread_mutex_unlock(&mtx);

(另见pthread_mutex_lockpthread_mutex_lock(3p))。当然,您应该在main

中进行相同的编码

锁定互斥锁有点贵(通常比添加多几十倍),即使在它被解锁的情况下也是如此。如果您可以在C11中编码,则可以考虑原子操作,因为您处理整数。您将声明atomic_int i;,并在其上使用atomic_loadatomic_fetch_add

如果您感到好奇,请参阅pthreads(7)&amp; futex(7)

多线程编程非常困难(适用于所有人)。您不能指望行为通常是可重现的,并且您的代码显然可以按预期运行并且仍然非常错误(并且在某些不同的系统上将以不同的方式工作)。另请阅读memory modelsCPU cachecache coherenceconcurrent computing ......

考虑使用GCC线程清理程序instrumentation options和/或valgrind&#39; s helgrind

答案 1 :(得分:1)

该问题与线程或全局变量无关,它与C&lt; 指针算术有关。

您可以使用以下代码获得完全相同的结果:

int main()
{
    int i = 5;
    int *j = 5;
    i = i + j;
    printf("%d\n", i); // this is 25
}

这里发生的是你将指针j分配给值5,&#34;添加5&#34;到那个指针。向指针添加5相当于在内存中添加足够的空间来容纳此指针指向的5个对象。在这种情况下,sizeof(int)是4,所以你真的加了4 * 5,即20。因此,结果是25,或5 + 4 * 5 = 25.

另一个警告,因为sizeof(int)取决于机器,你的结果可能会有所不同。

让我举一个例子来说明这一点:

int main()
{
    int i = 5;
    uint64_t *j = 5;
    i = i + j;
    printf("%d\n", i); // result is 45
}

因为sizeof(uint64_t)是8,这相当于将5 * 8添加到原始值5,因此结果是5 + 5 * 8 = 45。

此代码演示了类型转换的许多问题。 &#34; X&#34;首先声明为&#34; int&#34;,强制转换为通用指针&#34; void *&#34;,并隐式转换为&#34; int *&#34;,然后强制转换为&# 34; INT&#34 ;.正如你已经在这里展示的那样,这些类型的铸造肯定会让你自己陷入困境。

相关问题