【问题标题】:can we define a variable before calling pthread-create() function我们可以在调用 pthread-create() 函数之前定义一个变量吗
【发布时间】:2019-09-29 19:57:36
【问题描述】:

我尝试在 C 语言和 Manjaro OS 上测试多线程。 我写了一个小代码,但我遇到了一个奇怪的问题

我执行了下面的简单代码,但没有得到预期的结果:

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define THREADS 4

void *routine1(void * x)
{
    int result =2;
    pthread_exit((void *)result);
}

int main ()
{
    int sum=0;
    int retval=0;
   pthread_t threads[THREADS];
    for ( int i=0;i<THREADS;i++)
        pthread_create(&threads[i], NULL, routine1, (void *)i );
   for (int i=0; i<THREADS; i++)
   {
     pthread_join(threads[i],&retval);
     sum+=retval;
   }
   printf("%d\n",sum);
   return 0;

}

上面代码的结果是:

2

但我希望在输出中看到 8 个值。经过几个小时的调试,我发现如果我在 pthread_create() 函数之后声明 sum 变量,代码将正常运行:

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define THREADS 4

void *routine1(void * x)
{
    int result =2;
    pthread_exit((void *)result);
}

int main ()
{
    int retval=0;
   pthread_t threads[THREADS];
   for ( int i=0;i<THREADS;i++)
   pthread_create(&threads[i], NULL, routine1, (void *)i );
   int sum=0;
   for (int i=0; i<THREADS; i++)
   {
        pthread_join(threads[i],&retval);
        sum+=retval;
   }
   printf("%d\n",sum);
   return 0;
}

代码的输出是:

 8

这是正确的答案。

我想知道为什么第一个代码是错误的?是因为 pthread_create() 函数吗?

注意:如果你运行这段代码,不要关心警告,它们都是关于强制转换的

【问题讨论】:

  • 忽略警告后果自负。

标签: c linux multithreading pthread-join


【解决方案1】:

不,这是不是的顺序。它是retval类型。当将&amp;retval 传递给pthread_join 时,它的类型/大小是错误的。

retvalint 时,它只有4 个字节,但[假设64 位编译],pthread_join 需要一个指向void * 的指针,即8 个字节。

这会导致未定义的行为,因为调用会将 8 个字节写入一个只有 4 个字节的变量。剩余的 4 个字节超出了retval 的大小,可能会覆盖堆栈上的任何内容

在您的第二个示例中存在相同的未定义行为,但我们只是“走运”并得到了预期的结果[可能是因为堆栈帧中的变量顺序发生了变化,以避免 UB 产生意外结果]。但是,两者都有 UB。

另外,当转换到/从指针时,我们应该使用long 而不是int [防止编译器警告]。

这是更正后的代码:

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define THREADS 4

void *
routine1(void *x)
{
#if 0
    int result = 2;
#else
    long result = 2;
#endif

    pthread_exit((void *) result);
}

int
main()
{
    int sum = 0;
#if 0
    int retval = 0;
#else
    void *retval;
#endif
    pthread_t threads[THREADS];

#if 0
    for (int i = 0; i < THREADS; i++)
        pthread_create(&threads[i], NULL, routine1, (void *) i);
#else
    for (long i = 0; i < THREADS; i++)
        pthread_create(&threads[i], NULL, routine1, (void *) i);
#endif

    for (int i = 0; i < THREADS; i++) {
        pthread_join(threads[i], &retval);
#if 0
        sum += retval;
#else
        sum += (long) retval;
#endif
    }

    printf("%d\n", sum);

    return 0;

}

【讨论】:

  • 先生,即使使用 long 也不会导致 ub?
  • @snr 否,使用long 可防止警告从不同大小的整数转换为指针[-Wint-to-pointer-cast]。在这里,这无关紧要,但在一般情况下,将指针转换为 int 可以/将切断高 32 位,因此如果它们不为零,则结果值将被截断。不是 UB,只是一个错误的值。
  • 不,我们应该使用uintptr_tlong 不保证是指针的大小。
  • @JL2210 这实际上是由架构决定的。请参阅:unix.org/version2/whatsnew/lp64_wp.html 并查看表格。这是所有现代拱门都遵循并围绕其设计的规范。 long 适用于除LLP64 之外的所有型号,并且 MS/Win* 使用该型号[而且,我不使用Windows ;-)]。几乎所有其他人都使用LP64 [long long 为 64]。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-21
  • 1970-01-01
相关资源
最近更新 更多