我试图通过限制4点进餐的哲学家的数量来解决哲学家的问题。在主函数中,我这样编写代码以创建线程。
for (i = 0; i < N; i++) {
pthread_create(&tidp[i], NULL, creatPhilosopher, (void *)&i);
}
但这是输出
philosopher 0 is thinking...
philosopher 0 is thinking...
philosopher 0 is thinking...
philosopher 0 is thinking...
philosopher 0 is thinking...
philosopher 0 is eating...
philosopher 0 is eating...
philosopher 0 is eating...
philosopher 0 is eating...
philosopher 0 is eating...
同时,我尝试了另一种方法
for (i = 0; i < N; i++) {
int* temp = (int*)malloc(sizeof(int));
*temp = i;
pthread_create(&tidp[i], NULL, creatPhilosopher, (void *)temp);
}
输出正确。
philosopher 4 is thinking...
philosopher 3 is thinking...
philosopher 2 is thinking...
philosopher 1 is thinking...
philosopher 0 is thinking...
philosopher 4 is eating...
philosopher 3 is eating...
philosopher 2 is eating...
philosopher 1 is eating...
philosopher 0 is eating...
philosopher 4 is thinking...
philosopher 3 is thinking...
philosopher 2 is thinking...
philosopher 1 is thinking...
那么,两种方式有什么区别?
答案 0 :(得分:2)
在第一种情况下,只有一个存储位置传递给所有不同的线程:变量i
的地址。因此,所有线程将从相同位置读取其编号。他们从那里读取的内容取决于他们何时读取该值。全部都读为0似乎有点奇怪,但是无论如何,这不是您想要的。
在第二种情况下,为每个线程分配一个不同内存位置。因此,每个线程从不同的位置读取数据,因此从不同的编号读取数据。
答案 1 :(得分:0)
在第一个示例中,您将每个线程的指针/引用/句柄传递给相同的变量。一个变量保存一个值。您也不会同步对该变量的访问(更不用说它可能在每个线程试图读取该变量之前被销毁),因此实际上所有赌注都没有了。
在第二个示例中,每个线程都获得具有正确值的i
的副本,并且由于变量是独立的对象,因此您也具有线程安全性。
您应该始终在考虑对象的生存期,所有权和访问模式。
答案 2 :(得分:0)
区别是未定义的行为。
for (i = 0; i < N; i++) {
pthread_create(&tidp[i], NULL, creatPhilosopher, (void *)&i);
}
无论您使用的C ++标准还是POSIX(其线程API),都不能保证createPhilosopher
将开始执行并读取在{{之前传递给它的指针的值。 1}}循环的迭代完成,因此新线程将读取for
的当前值。无法保证i
实际上会在createPhilosopher
返回主执行线程之前真正开始执行。可能会或可能不会。这取决于月亮的相位和潮汐。
当执行线程开始执行时,整个pthread_create
循环很可能已经完成,并且只是一个遥远的内存。它多次调用for
(但是它们的执行线程尚未开始执行),实际上,pthread_create
变量现在已被销毁并且不再存在(无论在何处声明),现在它的内存已被其他一些随机选择的值占用,或者是垃圾,因此当新的执行线程读取它们作为参数接收的指针时,它就指向垃圾。
在另一种情况下,您将在动态范围内分配单个i
值,当执行线程实际开始执行时,该值仍然存在,因此它们读取该值。 int
没有它们,因此指针仍然是值。而且您泄漏了内存,但这是另一个故事。