交替打印字符串
描述
编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,但是:
如果这个数字可以被 3 整除,输出 "fizz"。
如果这个数字可以被 5 整除,输出 "buzz"。
如果这个数字可以同时被 3 和 5 整除,输出 "fizzbuzz"。
例如,当 n = 15,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz。
假设有这么一个类:
class FizzBuzz {
public FizzBuzz(int n) { ... } // constructor
public void fizz(printFizz) { ... } // only output "fizz"
public void buzz(printBuzz) { ... } // only output "buzz"
public void fizzbuzz(printFizzBuzz) { ... } // only output "fizzbuzz"
public void number(printNumber) { ... } // only output the numbers
}
请你实现一个有四个线程的多线程版 FizzBuzz, 同一个 FizzBuzz 实例会被如下四个线程使用:
线程A将调用 fizz() 来判断是否能被 3 整除,如果可以,则输出 fizz。
线程B将调用 buzz() 来判断是否能被 5 整除,如果可以,则输出 buzz。
线程C将调用 fizzbuzz() 来判断是否同时能被 3 和 5 整除,如果可以,则输出 fizzbuzz。
线程D将调用 number() 来实现输出既不能被 3 整除也不能被 5 整除的数字。
方案一(结果超时,暂时还不知道为什么)
- 使用信号量和互斥锁共同控制
typedef struct {
int n;
int current;
pthread_mutex_t mutex;
pthread_cond_t cond;
} FizzBuzz;
FizzBuzz* fizzBuzzCreate(int n) {
FizzBuzz* obj = (FizzBuzz*) malloc(sizeof(FizzBuzz));
obj->n = n;
obj->current = 1;
pthread_mutex_init(&obj->mutex, NULL);
pthread_cond_init(&obj->cond, NULL);
return obj;
}
// printFizz() outputs "fizz".
void fizz(FizzBuzz* obj) {
while(1)
{
pthread_mutex_lock(&obj->mutex);
if(obj->current > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
while(obj->current % 3 != 0 || obj->current % 5 == 0)
{
pthread_cond_wait(&obj->cond, &obj->mutex);
if(obj->current > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
}
printFizz();
obj->current++;
pthread_mutex_unlock(&obj->mutex);
pthread_cond_broadcast(&obj->cond);
}
}
// printBuzz() outputs "buzz".
void buzz(FizzBuzz* obj) {
while(1)
{
pthread_mutex_lock(&obj->mutex);
if(obj->current > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
while(obj->current % 3 == 0 || obj->current % 5 != 0)
{
pthread_cond_wait(&obj->cond, &obj->mutex);
if(obj->current > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
}
printBuzz();
obj->current++;
pthread_mutex_unlock(&obj->mutex);
pthread_cond_broadcast(&obj->cond);
}
}
// printFizzBuzz() outputs "fizzbuzz".
void fizzbuzz(FizzBuzz* obj) {
while(1)
{
pthread_mutex_lock(&obj->mutex);
if(obj->current > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
while(obj->current % 3 != 0 || obj->current % 5 != 0)
{
pthread_cond_wait(&obj->cond, &obj->mutex);
if(obj->current > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
}
printFizzBuzz();
obj->current++;
pthread_mutex_unlock(&obj->mutex);
pthread_cond_broadcast(&obj->cond);
}
}
// You may call global function `void printNumber(int x)`
// to output "x", where x is an integer.
void number(FizzBuzz* obj) {
while(1)
{
pthread_mutex_lock(&obj->mutex);
if(obj->current > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
while(obj->current % 3 == 0 || obj->current % 5 == 0)
{
pthread_cond_wait(&obj->cond, &obj->mutex);
if(obj->current > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
}
printNumber(obj->current);
obj->current++;
pthread_mutex_unlock(&obj->mutex);
pthread_cond_broadcast(&obj->cond);
}
}
void fizzBuzzFree(FizzBuzz* obj) {
if(obj)
{
pthread_mutex_destroy(&obj->mutex);
pthread_cond_destroy(&obj->cond);
free(obj);
obj = NULL;
}
}
方案二(报错:You called method from wrong thread. Exiting.)
- 由于每个函数不是打印一次,所以需要用while循环while(obj->index <= obj->n) {}
- 由于是满足一定的条件才能打印,因此调用pthread_cond_wait阻塞住当前线程,如果满足条件,才能往下运行
- 如果当前索引越界了,则停止线程,这一点很重要,不然会导致超时
- 如果索引有效,则打印相关的值,并唤醒其他线程工作
- 最后唤醒所有的线程,让每个线程结束运行。
typedef struct {
int n;
int index;
pthread_mutex_t mutex;
pthread_cond_t fizzs,buzzs,fizzbuzzs,numbers;
} FizzBuzz;
FizzBuzz* fizzBuzzCreate(int n) {
FizzBuzz* obj = (FizzBuzz*) malloc(sizeof(FizzBuzz));
obj->n = n;
obj->index = 1;
pthread_mutex_init(&obj->mutex, NULL);
pthread_cond_init(&obj->fizzs,NULL);
pthread_cond_init(&obj->buzzs,NULL);
pthread_cond_init(&obj->fizzbuzzs,NULL);
pthread_cond_init(&obj->numbers,NULL);
return obj;
}
void process (FizzBuzz* obj) {
if (obj->index > obj->n) {
pthread_cond_signal(&obj->fizzbuzzs);
pthread_cond_signal(&obj->fizzs);
pthread_cond_signal(&obj->buzzs);
pthread_cond_signal(&obj->numbers);
}
if (0 == obj->index%3 && 0 == obj->index%5) {
pthread_cond_signal(&obj->fizzbuzzs);
} else if (0 == obj->index%3) {
pthread_cond_signal(&obj->fizzs);
} else if (0 == obj->index%5) {
pthread_cond_signal(&obj->buzzs);
} else {
pthread_cond_signal(&obj->numbers);
}
}
// printFizz() outputs "fizz".
void fizz(FizzBuzz* obj) {
while(obj->index <= obj->n) {
pthread_mutex_lock(&obj->mutex);
while (0 != obj->index%3 && obj->index <= obj->n) {
pthread_cond_wait(&obj->fizzs,&obj->mutex);
}
if (obj->index > obj->n) {
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
} else {
printFizz();
obj->index++;
process(obj);
pthread_mutex_unlock(&obj->mutex);
}
}
}
// printBuzz() outputs "buzz".
void buzz(FizzBuzz* obj) {
while(obj->index <= obj->n) {
pthread_mutex_lock(&obj->mutex);
while (0 != obj->index%5 && obj->index <= obj->n) {
pthread_cond_wait(&obj->buzzs,&obj->mutex);
}
if (obj->index > obj->n) {
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
} else {
printBuzz();
obj->index++;
process(obj);
pthread_mutex_unlock(&obj->mutex);
}
}
}
// printFizzBuzz() outputs "fizzbuzz".
void fizzbuzz(FizzBuzz* obj) {
while(obj->index <= obj->n) {
pthread_mutex_lock(&obj->mutex);
while (0 != obj->index%5 && 0 != obj->index%3 && obj->index <= obj->n) {
pthread_cond_wait(&obj->fizzbuzzs,&obj->mutex);
}
if (obj->index > obj->n) {
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
} else {
printFizzBuzz();
obj->index++;
process(obj);
pthread_mutex_unlock(&obj->mutex);
}
}
}
// You may call global function `void printNumber(int x)`
// to output "x", where x is an integer.
void number(FizzBuzz* obj) {
while(obj->index <= obj->n) {
pthread_mutex_lock(&obj->mutex);
while((0 == obj->index%3 || 0 == obj->index%5) && obj->index <= obj->n) {
pthread_cond_wait(&obj->numbers,&obj->mutex);
}
if (obj->index > obj->n) {
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
} else {
printNumber(obj->index);
obj->index++;
process(obj);
pthread_mutex_unlock(&obj->mutex);
}
}
}
void fizzBuzzFree(FizzBuzz* obj) {
if (NULL != obj) {
pthread_mutex_destroy(&obj->mutex);
pthread_cond_destroy(&obj->fizzs);
pthread_cond_destroy(&obj->buzzs);
pthread_cond_destroy(&obj->fizzbuzzs);
pthread_cond_destroy(&obj->numbers);
}
}
方案三
typedef struct {
int n;
pthread_cond_t cond;
pthread_mutex_t mutex;
int cnt;
} FizzBuzz;
FizzBuzz* fizzBuzzCreate(int n) {
FizzBuzz* obj = (FizzBuzz*) malloc(sizeof(FizzBuzz));
obj->n = n;
obj->cnt = 1;
pthread_cond_init(&obj->cond, NULL);
pthread_mutex_init(&obj->mutex, NULL);
return obj;
}
// printFizz() outputs "fizz".
void fizz(FizzBuzz* obj) {
while(1)
{
pthread_mutex_lock(&obj->mutex);
if(obj->cnt > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
while((obj->cnt % 3 != 0) || (obj->cnt % 5 == 0))
{
pthread_cond_wait(&obj->cond, &obj->mutex);
if(obj->cnt > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
}
printFizz();
obj->cnt++;
pthread_mutex_unlock(&obj->mutex);
pthread_cond_broadcast(&obj->cond);
}
}
// printBuzz() outputs "buzz".
void buzz(FizzBuzz* obj) {
while(1)
{
pthread_mutex_lock(&obj->mutex);
if(obj->cnt > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
while((obj->cnt % 3 == 0) || (obj->cnt % 5 != 0))
{
pthread_cond_wait(&obj->cond, &obj->mutex);
if(obj->cnt > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
}
printBuzz();
obj->cnt++;
pthread_mutex_unlock(&obj->mutex);
pthread_cond_broadcast(&obj->cond);
}
}
// printFizzBuzz() outputs "fizzbuzz".
void fizzbuzz(FizzBuzz* obj) {
while(1)
{
pthread_mutex_lock(&obj->mutex);
if(obj->cnt > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
while((obj->cnt % 3 != 0) || (obj->cnt % 5 != 0))
{
pthread_cond_wait(&obj->cond, &obj->mutex);
if(obj->cnt > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
}
printFizzBuzz();
obj->cnt++;
pthread_mutex_unlock(&obj->mutex);
pthread_cond_broadcast(&obj->cond);
}
}
// You may call global function `void printNumber(int x)`
// to output "x", where x is an integer.
void number(FizzBuzz* obj) {
while(1)
{
pthread_mutex_lock(&obj->mutex);
if(obj->cnt > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
while((obj->cnt % 3 == 0) || (obj->cnt % 5 == 0))
{
pthread_cond_wait(&obj->cond, &obj->mutex);
if(obj->cnt > obj->n)
{
pthread_mutex_unlock(&obj->mutex);
pthread_exit(NULL);
}
}
printNumber(obj->cnt);
obj->cnt++;
pthread_mutex_unlock(&obj->mutex);
pthread_cond_broadcast(&obj->cond);
}
}
void fizzBuzzFree(FizzBuzz* obj) {
if(obj)
{
pthread_cond_destroy(&obj->cond);
pthread_mutex_destroy(&obj->mutex);
free(obj);
obj = NULL;
}
}