我正在研究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答案 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_lock和pthread_mutex_lock(3p))。当然,您应该在main
。
锁定互斥锁有点贵(通常比添加多几十倍),即使在它被解锁的情况下也是如此。如果您可以在C11中编码,则可以考虑原子操作,因为您处理整数。您将声明atomic_int i;
,并在其上使用atomic_load和atomic_fetch_add。
如果您感到好奇,请参阅pthreads(7)&amp; futex(7)
多线程编程非常困难(适用于所有人)。您不能指望行为通常是可重现的,并且您的代码显然可以按预期运行并且仍然非常错误(并且在某些不同的系统上将以不同的方式工作)。另请阅读memory models,CPU cache,cache coherence,concurrent 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 ;.正如你已经在这里展示的那样,这些类型的铸造肯定会让你自己陷入困境。