zoukankan      html  css  js  c++  java
  • 多线程之间同步

    1、POSIX信号量

    #include<semaphore.h>
    int sem_init(sem_t* sem, int pshared, unsigned int value);
    int sem_destroy(sem_t* sem);
    int sem_wait(sem_t* sem);
    sem_trywait(sem_t* sem);
    int sem_post(sem_t* sem);

        这组函数的第一个参数sem标识被操作的信号量。下面分别介绍每个函数:

    • sem_init函数用于初始化一个未命名信号量。pshared参数指定信号量类型,如果为0,则表示该信号量只在当前进程中共享,即局部信号量,否则该信号量可以在多个进程中共享;value参数指定信号量初始值。如果一个信号量已经被初始化,则再次初始化则会导致不可预期的结果。
    • sem_destroy函数用于销毁一个信号量。如果销毁一个正被其他线程等待的信号量,将导致不可预期的结果。
    • sem_wait函数以原子操作方式将信号量值减1,如果信号量为0,则sem_wait函数将被阻塞,直到这个信号量为非0。
    • sem_trywait函数与sem_wait函数功能类似,但它为非阻塞的,当信号量值为0时,它将返回-1,并设置errno为EAGAIN。
    • sem_post函数以原子操作方式将信号量值加1,如果信号量的值大于0,则其它等待该信号量的线程将被唤醒。

    2.1、互斥锁

        互斥锁用户保护临界区代码,保证任一时刻只有一个线程对临界区进行独占式访问,这与二进制信号量功能类似。互斥锁相关函数如下:

    #include<pthread.h>
    int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* mutexattr);
    int pthread_mutex_destroy(pthread_mutex_t* mutex);
    int pthread_mutex_lock(pthread_mutex_t* mutex);
    int pthread_mutex_trylock(pthread_mutex_t* mutex);
    int pthread_mutex_unlock(pthread_mutex_t* mutex);

    • 第一个参数mutex标识要操作的互斥锁。
    • pthread_mutex_init函数用于初始化互斥锁,mutexattr参数指定互斥锁的属性,如果设置为NULL,则表示使用默认属性。初始化互斥锁还可以使用另一种方式:
    pthread_mutex = PTHREAD_MUTEX_INITIALIZER  //将互斥锁的各个字段初始化为0
    • pthread_mutex_destroy函数用于销毁互斥锁,销毁一个已经加锁的互斥锁将导致不可预期的结果。
    • pthread_mutex_lock函数以原子操作方式给一个互斥锁(普通锁)加锁,如果该互斥锁已经加锁,则函数调用将阻塞,直到该互斥锁被解锁。
    • pthread_mutex_trylock与pthread_mutex_lock功能类似,但是是非阻塞的。当目标互斥锁已经被加锁,则返回错误码EBUSY。
    • pthread_mutex_unlock函数以原子操作方式给一个互斥锁解锁。

    2.2、互斥锁属性

        pthread_mutexattr_t结构定义了互斥锁的属性,并且线程库提供一系列的函数来操作该结构。这里不罗列这些函数,只介绍互斥锁较为重要的属性。

    1. pshared指定是否允许跨进程共享互斥锁,可选值有二:

    • PTHREAD_PROCESS_SHARED,互斥锁可以被跨进程共享
    • PTHREAD_PROCESS_PRIVATE,互斥锁只能在一个进程中共享   

        2. type指定互斥锁的类型,主要的几个类型如下:

    • PTHREAD_MUTEX_NORMAL,普通锁,默认情况也为普通锁。如果一个线程对一个普通锁加锁,则其余的请求该锁的线程将形成一个队列,当拥有该锁的线程解锁后,队列里的线程将按照优先级来获得该锁。这种锁保证了资源分配的公平性,但是也容易引起问题:一个线程如果对一个自己已经加锁的普通锁再次加锁,则引发死锁;对一个已经被其他线程加锁的普通锁解锁,或者对一个已经解锁的普通锁再次解锁,将导致不可预期结果。
    • PTHREAD_MUTEX_ERRORCHECK,检错锁。一个线程如果对一个自己已经加锁的检错锁进行加锁,则返回EDEADLK。对一个已经被其他线程加锁的检错锁解锁,或者对一个已经解锁的检错锁再次解锁,则操作将返回EPERM。
    • PTHREAD_MUTEX_RECURSIVE,嵌套锁。这种锁允许一个线程在解锁之前多次对它加锁而不发生死锁,不过其他线程如果想要获得该嵌套锁,则该嵌套锁的拥有者必须执行相应次数的解锁操作。对一个已经被其他线程加锁的嵌套锁解锁,或者对一个已经解锁的嵌套锁再次加锁,则会返回EPERM。

    3、条件变量

        条件变量用于线程之间同步共享数据的值,即当某个共享数据达到某个值的时候,唤醒等待这个共享数据的线程。

    #include<pthread.h>
    int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* cond_attr);
    int pthread_cond_destroy(pthread_cond_t* cond);
    int pthread_cond_broadcast(pthread_cond_t* cond);
    int pthread_cond_signal(pthread_cond_t* cond);
    int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);

    • 函数的第一个参数cond标识要操作的目标条件变量。
    • pthread_cond_init函数用于初始化条件变量,cond_attr为条件变量的属性,如果为NULL,则为默认属性。初始化条件变量还可以使用类似互斥锁的另一种方式,使用宏PTHREAD_COND_INITIALIZER,将条件变量的各个字段初始化为0。
    • pthread_cond_destroy函数用于销毁条件变量,如果销毁一个正在被等待的条件变量将失败并返回EBUSY。
    • pthread_cond_broadcast函数以广播的形式唤醒所有等待该条件变量的线程。
    • pthread_cond_signal函数用于唤醒一个等待目标条件变量的线程,具体唤醒哪个线程,取决于线程的优先级和调度策略。如果想唤醒指定的线程,那么可以采用定义全局变量来标识目标线程,然后使用广播的形式唤醒所有线程,线程被唤醒检查全局变量,判断是否自己被唤醒,如果不是则继续等待。
    • pthread_cond_wait函数用于等待条件变量,mutex参数是用于保护条件变量的互斥锁,以确保pthread_cond_wait操作的原子性。函数调用前,先对条件变量加锁,函数调用将现程放入等待条件变量的队列中,然后将互斥锁解锁,这样做就保证了pthread_cond_wait开始执行到线程被放入等待队列的这段时间内,条件变量不会被修改。当pthread_cond_wait函数成功返回时,互斥锁mutex将再次被锁上。

    4、线程同步机制包装类

    ifndef LOCKER_H
    #define LOCKER_H
    
    #include<exception>
    #include<pthread.h>
    #include<semaphore.h>
    
    /*封装信号量*/
    class sem
    {
    	public:
    		/*创建并初始化信号量*/
    		sem()
    		{
    			if(sem_init(&m_sem, 0, 0) != 0)
    			{
    				/*构造函数没有返回值,可以通过抛出异常来报告错误*/
    				throw std::exception();
    			}
    		}
    		/*销毁信号量*/
    		~sem()
    		{
    			sem_destroy(&m_sem);
    		}
    
    		/*等待信号量*/
    		bool wait()
    		{
    			return sem_wait(&m_sem) == 0;
    		}
    
    		/*增加信号量*/
    		bool post()
    		{
    			return sem_post(&m_sem) == 0;
    		}
    
    	private:
    		sem_t m_sem;
    };
    
    
    /*封装互斥锁*/
    class locker
    {
    	public:
    		/*创建并初始化互斥锁*/
    		locker()
    		{
    			if(pthread_mutex_init(&m_mutex, NULL) != 0)
    			{
    				throw std::exception();
    			}
    		}
    		/*销毁互斥锁*/
    		~locker()
    		{
    			pthread_mutex_destroy(&m_mutex);
    		}
    		/*获取互斥锁*/
    		bool lock()
    		{
    			return pthread_mutex_lock(&m_mutex) == 0;
    		}
    		/*释放互斥锁*/
    		bool unlock()
    		{
    			return pthread_mutex_unlock(&m_mutex) == 0;
    		}
    
    	private:
    		pthread_mutex_t m_mutex;
    };
    
    /*封装条件变量类*/
    class cond
    {
    	public:
    		/*创建并初始化条件变量*/
    		cond()
    		{
    			if(pthread_mutex_init(&m_mutex, NULL) != 0)
    			{
    				throw std::exception();
    			}
    			if(pthread_cond_init(&m_cond, NULL) != 0)
    			{
    				pthread_mutex_destroy(&m_mutex);
    				throw std::exception();
    			}
    		}
    		/*销毁条件变量*/
    		~cond()
    		{
    			pthread_mutex_destroy(&m_mutex);
    			pthread_cond_destroy(&m_cond);
    		}
    		/*等待条件变量*/
    		bool wait()
    		{
    			int ret = 0;
    			pthread_mutex_lock(&m_mutex);
    			ret = pthread_cond_wait(&m_cond, &m_mutex);
    			pthread_mutex_unlock(&m_mutex);
    			return ret == 0;
    		}
    		/*唤醒等待条件变量的线程*/
    		bool signal()
    		{
    			return pthread_cond_signal(&m_cond) == 0;
    		}
    
    	private:
    		pthread_mutex_t m_mutex;
    		pthread_cond_t m_cond;
    };
    
    #endif

  • 相关阅读:
    HIVE 2.1.0 安装教程。(数据源mysql)
    Linux基础命令—sleep
    Linux基础命令—echo
    C语言的基本数据类型
    Linux基础命令—rmdir
    Linux基础命令—mkdir
    Linux基础命令—cd
    Linux基础命令—pwd
    Linux周期性执行任务(crontab)
    Linux执行单一时刻定时任务管理操作(at)
  • 原文地址:https://www.cnblogs.com/fangyan5218/p/10618250.html
Copyright © 2011-2022 走看看