我有4个线程(Thread_A - Thread_D)。我希望它们以A,B,C,D的顺序结束。必须用信号量来解决。
我的代码有什么问题?在大多数情况下,它很好,但有时它的顺序错误。
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <semaphore.h>
void* thread_A (void *arg);
void* thread_B (void *arg);
void* thread_C (void *arg);
void* thread_D (void *arg);
typedef struct sem_package {
sem_t* sem1;
sem_t* sem2;
} sem_package;
void* thread_A (void *arg) {
sem_t* sem = (sem_t*) arg;
int random_sleep = (int) (500 + ((random()) / RAND_MAX) * 2000);
struct timespec sleep_time;
sleep_time.tv_sec = random_sleep / 1000;
sleep_time.tv_nsec = (random_sleep % 1000) * 1000000;
nanosleep(&sleep_time, NULL);
sem_post(sem); //unblock B
printf("\nA\n");
return NULL;
}
void* thread_B (void *arg) {
sem_package* pack = (sem_package*) arg;
int random_sleep = (int) (500 + ((random()) / RAND_MAX) * 2000);
struct timespec sleep_time;
sleep_time.tv_sec = random_sleep / 1000;
sleep_time.tv_nsec = (random_sleep % 1000) * 1000000;
nanosleep(&sleep_time, NULL);
sem_wait(pack->sem1); //wait for A
sem_post(pack->sem2); //unblock C
printf("\nB\n");
return NULL;
}
void* thread_C (void *arg) {
sem_package* pack = (sem_package*) arg;
int random_sleep = (int) (500 + ((random()) / RAND_MAX) * 2000);
struct timespec sleep_time;
sleep_time.tv_sec = random_sleep / 1000;
sleep_time.tv_nsec = (random_sleep % 1000) * 1000000;
nanosleep(&sleep_time, NULL);
sem_wait(pack->sem2); //wait for B
sem_post(pack->sem1); //unblock D
printf("\nC\n");
return NULL;
}
void* thread_D (void *arg) {
sem_t* sem = (sem_t*) arg;
int random_sleep = (int) (500 + ((random()) / RAND_MAX) * 2000);
struct timespec sleep_time;
sleep_time.tv_sec = random_sleep / 1000;
sleep_time.tv_nsec = (random_sleep % 1000) * 1000000;
nanosleep(&sleep_time, NULL);
sem_wait(sem); //wait for C
printf("\nD\n");
return NULL;
}
int main () {
srandom((unsigned int) time(NULL));
pthread_t threadA, threadB, threadC, threadD;
sem_t sem_A_B;
sem_t sem_C_D;
sem_t sem_B_C;
sem_init(&sem_A_B,0,0);
sem_init(&sem_C_D,0,0);
sem_init(&sem_B_C,0,0);
struct sem_package pack1;
pack1.sem1=&sem_A_B;
pack1.sem2=&sem_B_C;
struct sem_package pack2;
pack2.sem1=&sem_C_D;
pack2.sem2=&sem_B_C;
pthread_create(&threadA,NULL,thread_A,&sem_A_B);
pthread_create(&threadB,NULL,thread_B,&pack1);
pthread_create(&threadC,NULL,thread_C,&pack2);
pthread_create(&threadD,NULL,thread_D,&sem_C_D);
long m;
pthread_join(threadD, (void **) &m);
sem_destroy(&sem_A_B);
sem_destroy(&sem_B_C);
sem_destroy(&sem_C_D);
pthread_detach(threadA);
pthread_detach(threadB);
pthread_detach(threadC);
return 0;
}
答案 0 :(得分:3)
在comments中,我注意到:
这与线程完成顺序的问题相关,但有一点是错误的是你没有将线程名称添加到
struct sem_package
以便你可以只有一个线程函数,可以从传入的参数中获取所需的所有信息。您需要做一些清理工作,但这会大大减少您的代码。
我也是suggested:
在
fflush()
之后和sem_wait()
之前打印加sem_post()
会给您一个很好的机会[以正确的顺序打印线程终止消息]。
[我]完全按照你说的做了,现在它总是按照相同的顺序。我不明白为什么,但它确实有效。
它之所以有效,是因为(1)线程无法打印,直到他们收到“去”为止。来自其前任的信号,如果他们有前任,并且(2)一次只能打印一个线程,因为他们在有权允许的情况下进行打印,并且在授予下一个线程许可之前进行打印。
我提到了不使用fflush()
的可能性,但这似乎是一个坏主意(在Mac OS X 10.11.4上试验GCC 6.1.0)。我还没有弄清楚为什么fflush()
来电是必要的(但请参阅&#39;始终检查错误&#39;以下部分)。
这里有一些代码实现了一个为所有人服务的线程函数。它使用C99功能(指定的初始值设定项)来初始化时间结构。顶部的#pragma
会抑制在Mac OS X上弃用sem_init()
和sem_destroy()
的警告(有关详细信息,请参阅Why are sem_init()
, sem_getvalue()
, sem_destroy()
deprecated on Mac OS X — and what replaces them?)。
/* Pragma needed on Mac OS X to suppress warnings about sem_init() and sem_destroy() */
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <semaphore.h>
static void *thread_function(void *arg);
typedef struct sem_package
{
sem_t *sem1;
sem_t *sem2;
char *name;
} sem_package;
static
void *thread_function(void *arg)
{
sem_package *pack = (sem_package *)arg;
int random_sleep = (int)(500 + ((random()) / RAND_MAX) * 2000);
struct timespec sleep_time = { .tv_sec = random_sleep / 1000,
.tv_nsec = (random_sleep % 1000) * 1000000
};
nanosleep(&sleep_time, NULL);
if (pack->sem1)
sem_wait(pack->sem1); // wait for predecessor
printf("\n%s\n", pack->name);
fflush(stdout);
if (pack->sem2)
sem_post(pack->sem2); // unblock successor
return NULL;
}
int main(void)
{
srandom((unsigned int)time(NULL));
pthread_t threadA, threadB, threadC, threadD;
sem_t sem_A_B;
sem_t sem_C_D;
sem_t sem_B_C;
sem_init(&sem_A_B, 0, 0);
sem_init(&sem_C_D, 0, 0);
sem_init(&sem_B_C, 0, 0);
struct sem_package pack1 = { NULL, &sem_A_B, "A" };
struct sem_package pack2 = { &sem_A_B, &sem_B_C, "B" };
struct sem_package pack3 = { &sem_B_C, &sem_C_D, "C" };
struct sem_package pack4 = { &sem_C_D, NULL, "D" };
pthread_create(&threadA, NULL, thread_function, &pack1);
pthread_create(&threadB, NULL, thread_function, &pack2);
pthread_create(&threadC, NULL, thread_function, &pack3);
pthread_create(&threadD, NULL, thread_function, &pack4);
void *vp;
pthread_join(threadD, &vp);
sem_destroy(&sem_A_B);
sem_destroy(&sem_B_C);
sem_destroy(&sem_C_D);
pthread_detach(threadA);
pthread_detach(threadB);
pthread_detach(threadC);
printf("\nAll done\n\n");
return 0;
}
示例输出:
A
B
C
D
All done
为了尝试找出为什么我看到没有fflush()
的不稳定行为,我在系统调用上添加了相对全面的错误检查。最初的运行是一个清醒的提醒,为什么检查系统调用的返回值是很重要的:
$ ./pthread-37
pthread-37: sem_init(): error (78) Function not implemented
$
坦率地说,我宁愿系统没有提供切入点,而且我会假装它在这里,但它并不是真的在这里#&## 34 ;.对于“弃用”而言,这是一个奇怪的含义。太;通常,这意味着&#34;它存在(并且有效)但未来可能会丢失&#34;。但是,至少有一个可靠的解释,为什么信号量似乎没有强制执行 - 他们不存在,所以他们不能执行命令。
这一切都适用于Mac。当我在使用GCC 4.8.4的Ubuntu 14.04 LTS VM中尝试它时,代码正常工作 - 即使错误检查和没有fflush()
调用。这是理智的行为。
对象课程:
sem_init()
。答案 1 :(得分:0)
我希望他们以A,B,C,D的顺序结束。
你必须更准确地了解你的意思。考虑以下可能的操作交错(时间从顶部到底部增加):
Thread A Thread B
sem_post(sem); //unblock B
sem_wait(pack->sem1); //wait for A
sem_post(pack->sem2); //unblock C
printf("\nB\n");
return NULL;
printf("\nA\n");
return NULL;
如果要进行上述交错,您将在B
之前打印A
,在线程A之前打印B“结束”(到达return
)。
现在考虑另一个交错:
Thread A Thread B
sem_post(sem); //unblock B
sem_wait(pack->sem1); //wait for A
sem_post(pack->sem2); //unblock C
printf("\nB\n");
printf("\nA\n");
return NULL;
return NULL;
这里仍在B
之前打印A
,但现在线程A在线程B之前“结束”。您也可以在A
之前打印B
,线程A在线程B之前或之后结束(我将交错作为练习留给您)。
那么答案是什么?
你可能感兴趣的不是“线程A在线程B之前结束”,而是“线程A在线程B之前结束其有用的工作”。如果我们将有用的工作定义为打印A
或B
,那么为了解决您的问题,您必须在唤醒另一个线程之前移动printf
调用。