我正在编写一个程序,它创建一个打印 10 个数字的线程。当它打印 5 个时,它等待并通知主线程,然后它继续接下来的 5 个数字
这是 test.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
int rem = 10;
int count = 5;
pthread_mutex_t mtx;
pthread_cond_t cond1;
pthread_cond_t cond2;
void *f(void *arg)
{
int a;
srand(time(NULL));
while (rem > 0) {
a = rand() % 100;
printf("%d\n",a);
rem--;
count--;
if (count==0) {
printf("time to wake main thread up\n");
pthread_cond_signal(&cond1);
printf("second thread waits\n");
pthread_cond_wait(&cond2, &mtx);
printf("second thread woke up\n");
}
}
pthread_exit(NULL);
}
int main()
{
pthread_mutex_init(&mtx, 0);
pthread_cond_init(&cond1, 0);
pthread_cond_init(&cond2, 0);
pthread_t tids;
pthread_create(&tids, NULL, f, NULL);
while(1) {
if (count != 0) {
printf("main: waiting\n");
pthread_cond_wait(&cond1, &mtx);
printf("5 numbers are printed\n");
printf("main: waking up\n");
pthread_cond_signal(&cond2);
break;
}
pthread_cond_signal(&cond2);
if (rem == 0) break;
}
pthread_join(tids, NULL);
}
程序的输出为:
main: waiting
//5 random numbers
time to wake main thread up
second thread waits
5 numbers are printed
main: waking up
自从我做了pthread_cond_signal(&cond2);
我以为线程会唤醒并打印其余的数字,但事实并非如此。任何想法为什么?提前致谢。
答案 0 :(得分:0)
这些问题已在评论中进行了总结,或者至少是其中的大部分。然而,为了将实际答案记录在案:
关于程序对共享变量和同步对象的使用几乎没有什么是正确的。它的行为是未定义的,观察到的具体表现只是众多可能行为中更可能的表现之一。
如果两个不同的线程在运行期间访问(读取或写入)同一个非原子对象,并且至少有一次访问是写入,那么所有访问都必须通过同步操作得到适当的保护。
这些有很多种,在 StackOverflow 答案中无法全面涵盖,但其中最常见的是使用互斥锁来保护访问。在这种方法中,在程序中创建了一个互斥锁,并指定用于保护对一个或多个共享变量的访问。每个想要访问这些变量之一的线程在这样做之前都会锁定互斥锁。在稍后的某个时间,线程解锁互斥锁,以免其他线程被永久阻止锁定互斥锁本身。
示例:
pthread_mutex_t mutex; // must be initialized before use
int shared_variable;
// ...
void *thread_one_function(void *data) {
int rval;
// some work ...
rval = pthread_mutex_lock(&mutex);
// check for and handle lock failure ...
shared_variable++;
// ... maybe other work ...
rval = pthread_mutex_unlock(&mutex);
// check for and handle unlock failure ...
// more work ...
}
在你的程序中,rem
和 count
变量都是线程间共享的,对它们的访问需要同步。您已经有了一个互斥锁,使用它来保护对这些变量的访问似乎是合适的。
条件变量之所以有这个名字,是因为它们旨在支持特定的线程交互模式:只有当某个条件(取决于其他线程执行的操作)时,一个线程才希望继续通过某个点线程,很满意。此类要求经常出现。可以通过繁忙循环来实现这一点,其中线程重复测试条件(使用适当的同步)直到它为真,但这很浪费。条件变量允许这样的线程暂停操作,直到有必要再次检查条件。
条件变量的正确使用模式应被视为对繁忙循环的修改和特化:
示例:
pthread_cond_t cv; // must be initialized before use
void *thread_two_function(void *data) {
int rval;
// some work ...
rval = pthread_mutex_lock(&mutex);
// check for and handle lock failure ...
while (shared_variable < 5) {
rval = pthread_cond_wait(&cv, &mutex);
// check for and handle wait failure ...
}
// ... maybe other work ...
rval = pthread_mutex_unlock(&mutex);
// check for and handle unlock failure ...
// more work ...
}
注意
pthread_cond_wait()
的线程锁定了指定的互斥锁。否则,调用会引发未定义的行为。pthread_cond_signal()
或 pthread_cond_broadcast()
调用的影响。您的程序在这方面存在多个问题,其中包括:
两个线程都在不同步的情况下访问共享变量rem
和count
,其中一些访问是写。因此,整个程序的行为是未定义的。常见的表现之一是线程不观察彼此对这些变量的更新,尽管事情似乎按预期工作也是可能的。或其他任何东西。
两个线程都调用 pthread_cond_wait()
而不保持互斥锁锁定。因此,整个程序的行为是未定义的。 “未定义”意味着“未定义”,但 UB 可能表现为,例如,一个或两个线程在 CV 发出信号后未能从它们的等待中返回。
两个线程都没有采用 CV 使用的标准模式。任何一个都没有明确的关联条件,线程肯定不会测试一个。这留下了“此简历已发出信号”的隐含条件,但这是不安全的,因为在等待之前无法对其进行测试。特别是,它留下了这个可能的事件链:
cond1
等待。cond1
信号。cond2
之前,主线程至少通过信号 cond2
一直运行。一旦(3)发生,程序就无法避免死锁。主线程中断循环并尝试加入第二个线程,同时第二个线程到达其 pthread_cond_wait()
调用并阻塞等待永远不会到达的信号。
即使前面几点中提到的问题得到纠正,这一系列事件也可能发生,并且它可以准确地表现出您报告的可观察到的行为。