我有函数display.c:
/* DO NOT EDIT THIS FILE!!! */
#include <stdio.h>
#include <unistd.h>
#include "display.h"
void display(char *str)
{
char *p;
for (p=str; *p; p++)
{
write(1, p, 1);
usleep(100);
}
}
和display.h是:
/* DO NOT EDIT THIS FILE!!! */
#ifndef __CEID_OS_DISPLAY_H__
#define __CEID_OS_DISPLAY_H__
void display(char *);
#endif
我的任务是使用pthreads以获得以下输出:
abcd
abcd
abcd
..
..
请注意,我不能编辑文件display.c或文件display.c.我必须使用互斥锁才能成功完成上面显示的输出。
以下代码块是我最终尝试最终达到我想要的结果:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <pthread.h>
#include "display.h"
pthread_t mythread1;
pthread_t mythread2;
pthread_mutex_t m1, m2;
void *ab(void *arg)
{
pthread_mutex_lock(&m1);
display("ab");
pthread_mutex_unlock(&m1);
}
void *cd(void *arg)
{
pthread_mutex_lock(&m1);
display("cd\n");
pthread_mutex_unlock(&m1);
}
int main(int argc, char *argv[])
{
pthread_mutex_init(&m1, NULL);
pthread_mutex_init(&m2, NULL);
int i;
for(i=0;i<10;i++)
{
pthread_create(&mythread1, NULL, ab, NULL);
pthread_create(&mythread2, NULL, cd, NULL);
}
pthread_join(mythread1, NULL);
pthread_join(mythread2, NULL);
pthread_mutex_destroy(&m1);
pthread_mutex_destroy(&m2);
return EXIT_SUCCESS;
}
上面代码的输出是这样的:
abcd
abcd
abcd
abcd
ababcd
cd
abcd
abcd
abcd
abcd
正如您所见,“ab”和“cd \ n”从不混合,但每次运行代码时输出都不同。我想确保每次运行此代码时输出都是:
abcd
abcd
abcd
十次。
我真的很困惑,因为我找不到任何我已经知道的解决方案。
答案 0 :(得分:2)
互斥锁不能(本身)解决您的问题。它可以防止你的两个线程同时运行,但它不能强制它们轮流。
除了互斥锁或一对信号量之外,您还可以使用条件变量。无论哪种方式,关键是要始终保持对哪个线程转变的感觉。
我自己,我认为信号量方法更容易理解和编码。每个信号量主要与不同的线程相关联。该线程必须锁定信号量才能继续。当它完成一次迭代时,它解锁另一个信号量以允许另一个线程继续,并循环返回以尝试再次锁定它自己的信号量(它还不能做)。另一个线程以相同的方式工作,但信号量角色反转。粗略地说,那将是:
sem_t sem1;
sem_t sem2;
// ...
void *thread1_do(void *arg) {
int result;
do {
result = sem_wait(&sem1);
// do something
result = sem_post(&sem2);
} while (!done);
}
void *thread2_do(void *arg) {
int result;
do {
result = sem_wait(&sem2);
// do something else
result = sem_post(&sem1);
} while (!done);
}
为简洁起见,省略了信号量初始化,错误检查等。
已更新以添加:
由于您现在添加必须使用互斥锁(大概是以非平凡的方式),下一个最好的方法是引入一个条件变量(与互斥锁一起使用)和一个普通的共享变量来跟踪哪个线程就是这样。然后每个线程等待条件变量以获取互斥锁,在互斥锁的保护下检查共享变量以查看它是否轮到它,如果是,则继续。粗略地说,那将是:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int whose_turn = 1;
// ...
void *thread1_do(void *arg) {
int result;
result = pthread_mutex_lock(&mutex);
while (1) {
if (whose_turn == 1) {
// do something
whose_turn = 2; // it is thread 2's turn next
}
// break from the loop if finished
result = pthread_cond_broadcast(&cond);
result = pthread_cond_wait(&cond, &mutex);
}
result = pthread_mutex_unlock(&mutex);
}
void *thread1_do(void *arg) {
int result;
result = pthread_mutex_lock(&mutex);
while (1) {
if (whose_turn == 2) {
// do something else
whose_turn = 1; // it is thread 1's turn next
}
// break from the loop if finished
result = pthread_cond_broadcast(&cond);
result = pthread_cond_wait(&cond, &mutex);
}
result = pthread_mutex_unlock(&mutex);
}
为简洁起见,再次省略了错误检查。
特别注意,当线程等待条件变量时,它会释放相关的互斥锁。它在从等待返回之前获取互斥锁。另请注意,每次迭代都会检查是否轮到它继续。这是必要的,因为等待条件变量的虚假唤醒是可能的。
答案 1 :(得分:1)
您可以使用条件变量在线程之间轮流:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int turn = 0;
void *ab(void *arg)
{
pthread_mutex_lock(&m1);
while (turn != 0)
pthread_cond_wait(&cond, &m1);
display("ab");
turn = 1;
pthread_mutex_unlock(&m1);
pthread_cond_signal(&cond);
}
void *cd(void *arg)
{
pthread_mutex_lock(&m1);
while (turn != 1)
pthread_cond_wait(&cond, &m1);
display("cd\n");
turn = 0;
pthread_mutex_unlock(&m1);
pthread_cond_signal(&cond);
}
另一个问题是,你加入与main()
线程中创建的最后两对线程,这些线程不一定是最后执行的线程。如果早期创建的线程没有完成,那么当它正在使用并退出整个过程时,您正在销毁互斥锁m1
。
答案 2 :(得分:-1)
考虑这种问题的方法:
for(i=0;i<10;i++)
{
ab();
cd();
}
根据显示的代码,这完全实现了您的目标。您的示例的问题在于您有效地阻止了任何同步,这甚至是您的目标!
假设在输出之前,你实际上想要做一些有用的事情来占用CPU时间,答案是你必须改变display()
的代码,这根本不适合并行化。适当的并发代码被设计为独立于其他代码工作,特别是它不应该与其他调用竞争资源(锁),它不应该依赖于它完成的顺序。
总之,你不能从中学到很多,这是一个不好的例子。为了改进代码(您不想改变的代码,但这是您的问题),请考虑不同线程竞争的资源是stdout
。如果他们各自写入自己的缓冲区,您可以创建线程,等待它们完成,然后才重新排序结果以便输出。