一些线程在大量调用时永远不会执行
Some threads never get execution when invoked in large amount
考虑以下程序,
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 等。为什么会这样?
Why this is happening?
因为您有数据竞争(未定义的行为)。
特别是这个声明:
printf("%d\n",++count);
在没有任何锁定的情况下修改全局(共享)变量。由于 ++
不会自动递增它,因此多个线程很可能读取相同的值(比如 1234),递增它,然后将更新后的值 并行存储 ,导致 1235 被重复打印(两次或更多次),并且一个或多个增量被 lost.
一个典型的解决方案是要么使用互斥锁来避免数据竞争,要么(很少)使用原子变量(保证原子增量)。当心:原子变量很难正确。您还没有准备好使用它们。
因为 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。但原子问题是为什么你没有打印出你期望的所有数字。
考虑以下程序,
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 等。为什么会这样?
Why this is happening?
因为您有数据竞争(未定义的行为)。
特别是这个声明:
printf("%d\n",++count);
在没有任何锁定的情况下修改全局(共享)变量。由于 ++
不会自动递增它,因此多个线程很可能读取相同的值(比如 1234),递增它,然后将更新后的值 并行存储 ,导致 1235 被重复打印(两次或更多次),并且一个或多个增量被 lost.
一个典型的解决方案是要么使用互斥锁来避免数据竞争,要么(很少)使用原子变量(保证原子增量)。当心:原子变量很难正确。您还没有准备好使用它们。
因为 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。但原子问题是为什么你没有打印出你期望的所有数字。