【问题标题】:Some threads never get execution when invoked in large amount一些线程在大量调用时永远不会执行
【发布时间】:2026-01-16 11:45:02
【问题描述】:

考虑以下程序,

static long count = 0;
void thread()
{
    printf("%d\n",++count);
}
int main()
{
    pthread_t t;
    sigset_t set;
    int i,limit = 30000;
    struct rlimit rlim;

    getrlimit(RLIMIT_NPROC, &rlim);
    rlim.rlim_cur = rlim.rlim_max;
    setrlimit(RLIMIT_NPROC, &rlim);

    for(i=0; i<limit; i++) {
        if(pthread_create(&t,NULL,(void *(*)(void*))thread, NULL) != 0) {
            printf("thread creation failed\n");
            return -1;
        }
    }
    sigemptyset(&set);
    sigsuspend(&set);
    return 0;
}

这个程序预计会打印 1 到 30000。但它有时会打印 29945、29999、29959 等。为什么会这样?

【问题讨论】:

标签: c linux multithreading pthreads


【解决方案1】:

因为count 不是原子的,所以在增量和后续打印中都有竞争条件。

您需要的指令是atomic_fetch_add,用于增加计数器并避免竞争条件。 cppreference 上的示例说明了您提出的确切问题。

您的示例只需稍作调整即可工作:

#include <stdio.h>
#include <signal.h>
#include <sys/resource.h>
#include <pthread.h>
#include <stdatomic.h>

static atomic_long count = 1;
void * thread(void *data)
{
    printf("%ld\n", atomic_fetch_add(&count, 1));
    return NULL;
}

int main()
{
    pthread_t t;
    sigset_t set;
    int i,limit = 30000;
    struct rlimit rlim;

    getrlimit(RLIMIT_NPROC, &rlim);
    rlim.rlim_cur = rlim.rlim_max;
    setrlimit(RLIMIT_NPROC, &rlim);

    for(i=0; i<limit; i++) {
        if(pthread_create(&t, NULL, thread, NULL) != 0) {
            printf("thread creation failed\n");
            return -1;
        }
    }
    sigemptyset(&set);
    sigsuspend(&set);
    return 0;
}

我进行了一些其他更改,例如修复线程函数签名和使用正确的 printf 格式来打印 long。但原子问题是为什么您没有打印出所有预期的数字。

【讨论】:

  • "你需要的指令是 atomic_fetch_add" -- 不,他需要一个互斥锁。原子变量适合初学者。
  • @EmployedRussian 不,他没有? atomic_fetch_add 会给他一份原子数据的副本,并原子地递增计数器。 Counters 明确地是用例 atomic_fetch_add 的用途。你有解释为什么它不起作用吗?互斥锁并不比原子更容易理解,互斥锁引擎盖下的原子。
  • 它适用于像这样的小程序,但不是解决几乎所有实际问题的合适解决方案。你在说:“来,用这颗手榴弹开瓶”。开瓶器是更合适的工具。
  • “不是解决几乎所有实际问题的合适解决方案” 它是一个计数器,原子计数器在线程编程中无处不在。它们是一整类低争用信号量的同步原语。只是必须同意在这里不同意。
【解决方案2】:

为什么会这样?

因为您有数据竞争(未定义的行为)。

特别是这样的声明:

printf("%d\n",++count);

修改全局(共享)变量而不进行任何锁定。由于++ 不会自动递增它,因此多个线程很可能读取相同的值(例如 1234)、递增它并并行存储更新的值,从而导致打印 1235重复(两次或更多次),并且一个或多个增量丢失

一个典型的解决方案是使用互斥锁来避免数据竞争,或者(很少)使用原子变量(保证原子增量)。当心:原子变量很难正确处理。您还没有准备好使用它们。

【讨论】: