linux多线程编程--使用条件变量的简单程序

问题描述:

初始状态i=3,j=7;线程1同时做i++和j--的操作,当i==j时,线程2开始工作,线程2工作完成后,线程1完成剩下的操作,使i=7,j=3.

先贴最终版的代码如下:

#include <iostream>
#include <pthread.h>

using namespace std;

/* 当声明条件变量时,要记住条件变量与谓词是“链接”在一起的,建议将一组
 *  不变量、谓词和它们的互斥量,以及一到多个条件变量封装为一个数据结构的
 *  元素,并仔细记录下它们的关系,以免混淆 */
typedef struct th_struct {
    int i, j; // i==j是谓词,与cond对应
    bool is_th2_done; // 该谓词与cond_2对应
    pthread_mutex_t lock; // 两个条件变量共用一个互斥量lock
    pthread_cond_t cond;
    pthread_cond_t cond_2; 
}glob_data;
 
glob_data data; // 共享数据及互斥量、条件变量声明为全局量

void* changeVar(void*);
void* doSomething(void*);

int main() {
    data.i = 3; data.j = 7; data.is_th2_done = false;
    pthread_mutex_init(&data.lock, NULL);
    pthread_cond_init(&data.cond, NULL); // 初始化条件变量
    pthread_cond_init(&data.cond_2, NULL);
    
    pthread_t thread1;
    /* 线程创建后,立即进入“就绪”状态,只要相应条件一满足,就从
     * changeVar入口函数开始执行 */
    pthread_create(&thread1, NULL, changeVar, NULL);
    
    pthread_t thread2;
    pthread_create(&thread2, NULL, doSomething, NULL);
    
    /*join函数等待相应线程执行,线程未执行完,调用join函数的主线程
     则处于阻塞状态,直到线程执行完为止,然后回收资源空间,
     * 此后线程ID不可用*/
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    
    pthread_cond_destroy(&data.cond); // 动态初始化的,必须destroy
    pthread_cond_destroy(&data.cond_2);
    pthread_mutex_destroy(&data.lock);
    return 0;
}

void* changeVar(void*) {
    pthread_mutex_lock(&data.lock);
    while (data.i<8) {
        cout << "thread1 run here!" << endl;
        cout << "now i= " << data.i << "j= " << data.j << endl;
        if (data.i==data.j) {
            cout << "go to thread2!" << endl;
            /* 通知等待条件变量的线程,如果有线程等待该条件变量,则“叫醒”等待的线程
             * 如果没有线程等待该条件变量,则什么也没发生 */
            pthread_cond_signal(&data.cond);  
            // 不可对已解锁的锁解锁
            pthread_mutex_unlock(&data.lock); // 最好先给信号,再释放锁
            
            /* 开始等待thread2的完成,cond_wait函数将阻塞调用它的该线程,
             * 直到有条件变量通知它共享数据的状态已经改变为止*/
            while (!data.is_th2_done) 
                pthread_cond_wait(&data.cond_2, &data.lock);
        }   
        ++data.i;
        --data.j;
    }
    pthread_exit(NULL); 
    /* exit调用后,线程中止,完成清理工作,此后线程中的数据已经“old”
     * 不可访问,但线程ID还可用 */
}

void* doSomething(void*) {
    /* 阻塞式加锁,如果加锁失败,阻塞该线程,并一直检测该锁的状态
     * 当锁被解开后,该线程恢复,重新加锁,加锁成功则执行之后的代码*/
    pthread_mutex_lock(&data.lock);
    // 等待谓词i==j的到来,如果条件不满足,则线程一直被阻塞
    while (data.i!=data.j) // 用while循环判断谓词是很有必要的
        pthread_cond_wait(&data.cond, &data.lock);
    if (data.i==data.j) {
        cout << endl;
        cout << "thread2 start!" << endl;
        cout << "thread2 end!" << endl;
        cout << endl;
        data.is_th2_done = true;
        // 通知线程1我已经完成
        pthread_cond_signal(&data.cond_2);
    }
    pthread_mutex_unlock(&data.lock);
    pthread_exit(NULL);
}
以上知识点来源于 《POSIX多线程程序设计》,这是本很好的书,将函数的原理讲得很透彻。

下面是我比较失败的版本,贴出来,然后分析:

<pre name="code" class="cpp">#include <iostream>
#include <pthread.h>

using namespace std;

int i(3), j(7);
pthread_mutex_t lock;
pthread_cond_t cond;

void* changeVar(void*);
void* doSomething(void*);

int main() {
    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&cond, NULL); // 初始化条件变量
    
    pthread_t thread1;
    pthread_create(&thread1, NULL, changeVar, NULL);
    
    pthread_t thread2;
    pthread_create(&thread2, NULL, doSomething, NULL);
    
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    
    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&lock);
    return 0;
}

void* changeVar(void*) {
    pthread_mutex_lock(&lock);
    cout << endl;
    while (i<8) {
        cout << "thread1 running!" << endl;
        cout << "i=" << i << " j=" << j << endl;
        if (i==j) {
            cout << "go to thread2!" << endl;
            pthread_mutex_unlock(&lock); // 释放锁
            pthread_cond_signal(&cond); // 通知等待条件变量的线程 
            sleep(0.1);
        }   
        ++i;
        --j;
    }
    pthread_mutex_unlock(&lock);
    pthread_exit(NULL);
}

void* doSomething(void*) {
    pthread_mutex_lock(&lock);
    if (i!=j) {
        pthread_mutex_unlock(&lock);
        pthread_cond_wait(&cond, &lock);   
    }
    cout << endl;
    cout << "thread2 start!" << endl;
    cout << "thread2 end!" << endl;
    pthread_mutex_unlock(&lock);
    pthread_exit(NULL);
}


 程序执行过程如下: 

1 刚开始,两个线程争夺锁资源,如果线程1胜利,goto 2,否则goto 5;

2 执行到i==j==5时,当有如下输出的时候:

thread1 running!
i=3 j=7
thread1 running!
i=4 j=6
thread1 running!
i=5 j=5
go to thread2!
thread1通知条件变量并解锁,然后它自己sleep;

3 被阻塞的thread2此时发现锁被解开后,对其加锁,然后检测i与j是否相等,因为它们相等,所以开始输出;

4 如果thread1睡眠时间很短,短到thread2来不及加锁,就会得到下面的结果:

thread1 run here!
thread1 run here!
thread1 run here!
go to thread2!
thread1 run here!
thread1 run here!

thread2 start!
thread2 end!
如果thread1睡眠较短,以致它cout的时候thread2也在cout,它们就会争夺cout的资源,出现下面的结果:

thread1 run here!
now i= 3j= 7
thread1 run here!
now i= 4j= 6
thread1 run here!
now i= 5j= 5
go to thread2!

thread2 start!
thread2 end!
thread1 run here!
now i= 6j= 4
thread1 run here!
now i= 7j= 3
如果thread1睡眠较长,就会让thread2先cout,然后thread1在做剩下的事情,这样输出的就是正确的结果。

5 当thread2先占据了锁资源,也能输出正确的结果。分析完毕!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值