重明鸟软件知识平台
@C/C++多线程编程(2)
这节我们来讲下线程间的通信。线程通信有两种方法,一种是用互斥锁,另一种是用条件条件变量。

一、互斥锁。

线程互斥意味着两个线程不能同时进入代码段,Linux可以通过互斥体pthread_mutex_t定义互斥机制完成互斥操作,互斥体机制是在同一时刻只能有一个线程掌握某个互斥上的锁,拥有上锁状态的线程能够对共享资源进行访问。若其他线程希望锁定一个已经上锁的互斥体,则该线程挂起,进入阻塞状态,直到上锁的线程释放互斥锁为止。

互斥锁的操作主要包括以下几个步骤:
  互斥锁初始化:pthread_mutex_init

互斥锁锁定:pthread_mutex_lock

互斥锁尝试锁定:pthread_mutex_trylock

互斥锁解锁:pthread_mutex_unlock

互斥锁销毁:pthread_mutex_destroy


互斥锁主要分为三种:

分为快速互斥锁,递归互斥锁,检索互斥锁:
这三种锁的主要区别在于其他为占有互斥锁的线程在希望得到互斥锁的时候是否需要阻塞等待:
快速互斥锁是指调用线程会阻塞直到拥有互斥锁的线程释放为止;
递归互斥锁能够成功返回并且增加调用线程在互斥上加锁的次数;
检索互斥锁则为快速互斥锁的阻塞版本,他会立即返回并得到一个错误。


Linux系统在缺省参数情况下创建的是快速互斥锁,而一般情况下windows系统是默认采用递归互斥锁的,所以多数有经验的linux开发人员都采用递归互斥锁,以保证和windows的一致性。当然,我们需要根据程序不同的情况定义所需的互斥锁。

//函数原型:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *mutexattr);

//快速互斥锁
Mutexattr:PTHREAD_MUTEX_INITIALIZER  

//递归互斥锁
Mutexattr:PTHREAD_RECRUSIVE_MUTEX_INITIALIZER_NP  

//检索互斥锁
Mutexattr:PTHREAD_REEORCHECK_MUTEX_INITIALIZER_NP

二、条件变量。
与互斥锁不同,条件变量是用来阻塞一个线程,直到某种特定条件发生为止。通常条件变量需要配合互斥锁同时使用。
条件变量使线程可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量通信的一种机制,一个条件变量主要有两种状态:unsignaled(不成立状态)和signaled(成立状态)。而线程间通信主要有两个动作:一个线程等待(条件变量的条件成立),不成立则挂起,另外一个线程使条件成立(给出条件成立的信号)。
条件的检测是在互斥锁保护下进行的,如果一个条件为假(unsignaled),一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另外一个线程改变条件使条件成立,它就会发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。

主要操作函数如下:
   pthread_cond_init条件变量初始化。

pthread_cond_wait阻塞等待:条件不成立,则线程将一直处于阻塞状态。

pthread_cond_timedwait超时等待,一定时间后程序自动解除阻塞状态。

pthread_cond_signal在使用这个函数的时候需要注意一个问题,就是linux条件变量自动复位的问题。此函数只将一个处于阻塞的线程解除阻塞状态,即使有多个线程等待此条件发生。

pthread_cond_broadcast将所有等待条件发生的线程解除阻塞状态。

pthread_cond_destroy销毁条件变量。

示例,我们以上一节的例子为示范,由于多线程并发执行不可确定,输出结果不理想,我们给线程加个互斥变量,保证每个线程结果输出完成后,再执行下一个线程操作。

main.cpp源码:
#include <iostream>
#include <sys/types.h>
#include <pthread.h> //多线程相关操作头文件,可移植众多平台

using namespace std;

#define NUM_THREADS 5 //线程数

//定义互斥变量
pthread_mutex_t mutex;

void* say_hello( void* args )
{
    //等待加锁互斥变量
    pthread_mutex_lock(&mutex);

    cout << "Hello,I am threads "<<pthread_self() << endl;

    //解锁互斥变量
    pthread_mutex_unlock(&mutex);
}

int main()
{
    //线程id
    pthread_t tids[NUM_THREADS];

    //初始化互斥变量
    pthread_mutex_init(&mutex,NULL);

    for( int i = 0; i < NUM_THREADS; ++i )
    {
        //参数:创建的线程id,线程参数,线程运行函数的起始地址,运行函数的参数
        int ret = pthread_create( &tids[i], NULL, say_hello, NULL );
        if( ret != 0 ) //创建线程成功返回0
        {
            cout << "create thread error code=" << ret << endl;
        }
    }

    //等待各个线程退出后,进程才结束,否则进程强制结束,线程处于未终止的状态
    pthread_exit( NULL );
}



运行结果:
12
@站内查找
@栏目文章
【基础教程】TKinter(5):Label控件
【基础教程】TKinter(4):Messagebox控件
【基础教程】TKinter(3):Button控件
【基础教程】TKinter(2):控件及其属性
【基础教程】TKinter(1):第一个tkinter程序
【基础教程】Python守护进程创建
【基础教程】PyGame(13):三维环境设置
【基础教程】C/C++指针函数和回调函数
【基础教程】C/C++多进程编程(3)
【基础教程】C/C++多进程编程(2)
【基础教程】C/C++多进程编程(1)
【基础教程】C/C++多线程编程(2)
【基础教程】C/C++多线程编程(1)
【基础教程】C/C++动态库编程
【基础教程】C/C++静态库编程
【基础教程】photoshop图片混合公式
【基础教程】PyGame(12):旋转和缩放
【基础教程】PyGame(11):Surface操作
【基础教程】网络编程:Epoll(2)
【基础教程】网络编程:Epoll(1)
【基础教程】网络编程:IOCP(3)
【基础教程】网络编程:IOCP(2)
【基础教程】网络编程:IOCP(1)
【基础教程】网络编程:UDP
【基础教程】网络编程:TCP
【基础教程】Ubuntu安装Hadoop(4)
【基础教程】Ubuntu安装Hadoop(3)
【基础教程】Ubuntu安装Hadoop(2)
【基础教程】Ubuntu安装Hadoop(1)
【基础教程】Hadoop技术原理
【基础教程】Hadoop基本命令
【基础教程】Hadoop是什么东西
【基础教程】排序算法介绍
【基础教程】PyGame(10):声音控制
【基础教程】PyGame(9):鼠标事件(2)
【基础教程】PyGame(8):鼠标事件(1)
【基础教程】PyGame(7):键盘事件(2)
【基础教程】PyGame(6):键盘事件(1)
【基础教程】PyGame(5):显示图片
【基础教程】PyGame(4):显示文字
【基础教程】PyGame(3):图元绘制
【基础教程】PyGame(2):游戏框架
【基础教程】PyGame(1):开发环境搭建
【基础教程】Python(10):类定义
【基础教程】Python(9):自定义模块
【基础教程】Python(8):输入输出控制
【基础教程】Python(7):函数定义
【基础教程】Python(6):中断语句
【基础教程】Python(5):判断语句
【基础教程】Python(4):循环语句
【基础教程】Python(3):变量与运算符
【基础教程】Python(2):Hello
【基础教程】Python(1):介绍与安装
Copyright @ 2011-2018 by szc  桂ICP备11003301号-1  桂公网安备45040302000027号  Email:szcsoft@qq.com