为什么c中的pthread是这样的



我的代码应该打印0 1 2 3 4,但为什么输出是从1到5。每次重新运行代码,它改变输出,但范围保持不变1-5输出我得到:1 2 3 5 5, 1 2 2 2 5 5, 5 5 5 5期望输出为:0 1 2 3 4谁能告诉我为什么我没有得到预期的输出

#include <stdio.h>
#include <pthread.h>
void* printInt(void*ptr)
{
int*iptr=(int*)ptr;
printf("%dn",*iptr);
}
int main()
{
pthread_t tid[5];
pthread_attr_t attr;
pthread_attr_init(&attr);
for(int i=0;i<5;i++)
{
pthread_create(&tid[i],&attr,printInt,(void*)&i);
}
for(int i=0;i<5;i++)
{
pthread_join(tid[i],NULL);
}
return 0;
}

在此调用

pthread_create(&tid[i],&attr,printInt,(void*)&i);

你传递了一个指向同一个局部变量i的指针,该指针可以在线程开始之前在循环中更改,而且程序可能具有未定义的行为,因为指向的变量可能在线程试图访问它时处于非活动状态。

不幸的是,您的代码有两个竞争条件,其中只有一个可以[容易地]修复。

  1. main中,通过指针传递i允许每个线程函数接收[看似]随机的值。这是因为给每个线程的指针指向相同的变量(即i)。例如,传递给线程0的值可以是1,因为main执行循环的速度比线程0启动的速度快。

  2. 即使我们修复了上面的问题(参见下面的方法),仅仅因为线程[通过pthread_create]按顺序创建的:0,1,2,3,4,不能保证它们将以相同的顺序执行/打印。例如,它们可以按以下顺序执行:3,0,2,1,4


要解决第一个问题,在main中,我们可以通过value:

传递i
pthread_create(&tid[i],&attr,printInt,(void *) (uintptr_t) i);

printInt的相应变化:

int i = (uintptr_t) ptr;
printf("%dn",i);

然而,如果没有线程之间的某种同步,第二个问题就无法解决,这会破坏使用单独线程的目的。

有不同的[同样混乱的]方法来做到这一点。

例如,main和线程可以通过一个或两个全局变量进行同步,这些全局变量是通过(例如)stdatomic.h原语存储/获取的,以使main"授予"。按顺序访问每个线程。并且,线程必须通过另一个全局变量确认完成。

或者,可以使用互斥锁和信号量。


这是一个[crude]同步方法,使用原子。我们创建了一个单任务struct来保存给定线程的数据:

#include <stdio.h>
#include <stdatomic.h>
#include <unistd.h>
#include <pthread.h>
struct tsk {
pthread_t tsk_tid;
int tsk_i;
int tsk_sync;
};
void *
printInt(void *ptr)
{
struct tsk *tsk = ptr;
// wait for main to give us the "go"
while (1) {
if (atomic_load(&tsk->tsk_sync) == 1)
break;
usleep(1);
}
printf("%dn", tsk->tsk_i);
fflush(stdout);
// tell main we're done
atomic_store(&tsk->tsk_sync,2);
return (void *) 0;
}
int
main(void)
{
struct tsk tsklist[5] = { 0 };
struct tsk *tsk;
pthread_attr_t attr;
pthread_attr_init(&attr);
for (int i = 0; i < 5; i++) {
tsk = &tsklist[i];
tsk->tsk_i = i;
atomic_store(&tsk->tsk_sync,0);
pthread_create(&tsk->tsk_tid, &attr, printInt, tsk);
}
// grant each thread its turn, and wait for it to acknowledge
for (int i = 0; i < 5; i++) {
tsk = &tsklist[i];
atomic_store(&tsk->tsk_sync,1);
while (atomic_load(&tsk->tsk_sync) == 1)
usleep(1);
}
for (int i = 0; i < 5; i++) {
tsk = &tsklist[i];
pthread_join(tsk->tsk_tid, NULL);
}
return 0;
}

可能有更好的方法来同步线程。

在这个方法中,主线程授予"访问"权限。并等待线程"完成"。


另一种方法是,一旦主线程授予对第一个线程(例如线程0)的访问权,那么该线程就可以运行,并且可以授予对列表中下一个线程的访问权(例如线程0授予对线程1的访问权,等等):

#include <stdio.h>
#include <stdatomic.h>
#include <unistd.h>
#include <pthread.h>
struct tsk {
pthread_t tsk_tid;
int tsk_i;
int tsk_sync;
};
int owner = -1;
void *
printInt(void *ptr)
{
struct tsk *tsk = ptr;
// wait for somebody to give us the "go"
while (1) {
if (atomic_load(&owner) == tsk->tsk_i)
break;
usleep(1);
}
printf("%dn", tsk->tsk_i);
fflush(stdout);
// say we're done and start the next thread
atomic_store(&owner,tsk->tsk_i + 1);
return (void *) 0;
}
int
main(void)
{
struct tsk tsklist[5] = { 0 };
struct tsk *tsk;
pthread_attr_t attr;
pthread_attr_init(&attr);
for (int i = 0; i < 5; i++) {
tsk = &tsklist[i];
tsk->tsk_i = i;
pthread_create(&tsk->tsk_tid, &attr, printInt, tsk);
}
// start first thread
atomic_store(&owner,0);
for (int i = 0; i < 5; i++) {
tsk = &tsklist[i];
pthread_join(tsk->tsk_tid, NULL);
}
return 0;
}

@Vlad已经给了你问题的原因。一种解决方案是在数组中传递keep要传递的值。

int param[5];
for (int i = 0; i < 5; ++i)
{
param[i] = i;
pthread_create(&tid[i], &attr, printInt, (void*)&param[i]);
}

这样在线程开始运行之前该值不会改变。

最新更新