不幸的是,您的代码有两个竞争条件,其中只有一个可以[轻松]修复。
-
在main 中,通过指针 传递i 允许每个线程函数接收[看似] 随机值。那是因为给每个线程的指针指向 same 变量(即i)。例如,传递给线程 0 的值可能是 1,因为 main 执行其循环的速度比线程 0 的启动速度要快。
-
即使我们修复了上面的问题[见下文],只是因为线程是创建的 [通过pthread_create]的顺序:0,1,2,3,4 no 保证它们将以相同的顺序执行/打印。例如,它们可以按以下顺序执行:3,0,2,1,4
为了解决第一个问题,在main,我们可以通过值传递i:
pthread_create(&tid[i],&attr,printInt,(void *) (uintptr_t) i);
而且,printInt 的相应变化:
int i = (uintptr_t) ptr;
printf("%d\n",i);
但是,如果线程之间没有某种同步,则无法解决第二个问题,这违背了拥有单独线程的目的。
有不同的[同样混乱的]方法可以做到这一点。
例如,main 和线程可以通过一个或两个全局变量进行同步,这些变量通过(例如)stdatomic.h 原语存储/获取,以使main 按顺序“授予”对每个线程的访问权限。而且,线程必须通过另一个全局确认完成。
或者,可以使用互斥体和信号量。
这是一种[粗略的]同步方法,使用原子。我们为每个任务创建一个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("%d\n", 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)的访问权限,那么线程就可以运行并且 it 可以授予对列表中的下一个线程的访问权限(例如线程 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("%d\n", 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;
}