首页 > 解决方案 > 使用使用 clone() 创建的线程时,线程本地存储不起作用

问题描述

下面的程序使用 Linux 的clone()系统调用创建了 10 个线程。静态变量tls具有 C11thread_local属性。线程执行该child_func函数,该函数只是增加tls变量并将增加的值存储在参数指向的位置arg。递增的值存储在数组中,每个线程一个tlsvals。该main函数等待线程完成,然后打印变量的十个实例的tls值。(我在这个版本的代码中跳过了错误检查和释放分配的堆栈,以保持示例简短。)

#define _GNU_SOURCE
#include <sched.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>        /* For thread_local. */

#define NTHREADS 10

static thread_local int tls = 0;

static int child_func(void *arg)
{
  tls++;
  *((int *)arg) = tls;
  return 0;
}

int main(int argc, char** argv)
{
  int tlsvals[NTHREADS];
  for (int n = 0; n < NTHREADS; n++) {
    const int STACK_SIZE = 65536;
    if (clone(child_func, malloc(STACK_SIZE) + STACK_SIZE, CLONE_VM | SIGCHLD, tlsvals+n) == -1) {
      perror("clone");
      exit(1);
    }
  }

  int ret, status;
  while ((ret = wait(&status)) != -1)
    ;

  for (int *p = tlsvals; p < tlsvals + NTHREADS; p++)
    printf("The value of tls is %d\n", *p);

  return 0;
}

由于tls变量标记为thread_local,我希望程序打印十行The value of tls is 1,因为每个线程都会从其初始零值增加一次变量。相反,我得到以下输出:

The value of tls is 1
The value of tls is 3
The value of tls is 2
The value of tls is 4
The value of tls is 5
The value of tls is 6
The value of tls is 7
The value of tls is 8
The value of tls is 9
The value of tls is 10

因此,该tls变量似乎根本不是线程本地的,而是由所有线程共享的,并且每个线程都会增加相同的变量。

该代码是使用以下命令行在 x86_64 上使用 GCC 版本 9.1.0 编译的:

gcc -O2 -Wall -std=c11 tfoo.c -o tfooc

我还尝试使用 GCC 特定__thread属性而不是thread_local,结果相同。

查看 GCC 生成的程序集,我可以看到变量是通过%fs寄存器访问的,它应该是:

child_func:
.LFB24:
    .cfi_startproc
    movl    %fs:tls@tpoff, %eax
    addl    $1, %eax
    movl    %eax, %fs:tls@tpoff
    movl    %eax, (%rdi)
    xorl    %eax, %eax
    ret
    .cfi_endproc

我在做什么错,或者thread_local在 Gcc 中的实现中是否存在错误?

标签: linuxmultithreadinggccc11thread-local-storage

解决方案


推荐阅读