【Linux】条件变量与信号量


场景
    服务器有多个线程要写文件,如果一起写的话,就会发生顺序混乱,有可能刚刚写好了ABC还没来得及更新,就被后面的DEF覆盖掉。
解决方案
    创建多个线程来读取服务器中的内容写到缓冲区,再创建一个线程专门读取缓冲区中的内容,缓冲区存满了就不能往里面写了,等读端读取部分数据
后通知写端可以往缓冲区中写入数据了,缓冲区空了就不能读取数据了,等待写端写入数据后通知它可以读了。
    
条件变量
    1.定义条件变量 pthread_cond_t cond;
    2.初始化 pthread_cond_init(&cond, NULL);  // 第二个条件是属性
    3.等待条件 pthread_cond_wait(&cond, &mutex);
        // 如果mutex没有在互斥环境,就形同虚设,在互斥环境中wait函数将其置为1(1表示打开锁,不管原来的值是多少),
        执行结束后,wait返回,mutex恢复成原来的值。
    4.修改条件 pthread_cond_signal(&cond);  // 激活等待条件,相当于一个通知
    5.销毁条件变量 pthread_cond_destroy(&cond);
    
    多个生产者往缓冲区中放入东西的时候,很有可能放到同一块地方,因此要求生产者之间是互斥关系。同理消费者也是如此。
        这就需要加上互斥锁来保证每次只有一个线程来操作。
                
                生产者
                
        int pthread_mutex_lock(pthread_mutex_t *mutex);
            生产
        int pthread_cond_signal(pthread_cond_t *cond);
        int pthread_mutex_unlock(pthread_mutex_t *mutex);
                
                消费者
        
        int pthread_mutex_lock(pthread_mutex_t *mutex);
        if (没有可消费)
            int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
        else
            消费
        int pthread_mutex_unlock(pthread_mutex_t *mutex);
        
        分析:如果此时消费者发现没有可以消费的,于是就在等待生产者生产,但是此时生产者不能生产(ps:此时生产者由于加锁,进不去)。
        于是就产生了死锁。而pthread_cond_wait的第二个参数是个互斥锁,可以将生产者的锁解开。
        
    规范写法
        pthread_mutex_lock();
        while (条件不满足)
            pthread_cond_wait();
        pthread_mutex_unlock();
            分析为什么用while:这是因为wait函数是个阻塞函数,很有可能被信号打断,而wait函数返回有两种情况,一种是被signal函数通知,
        一种是被别的信号打断,设置成while后,如果是被信号打断,那么再次进入条件判断时,仍然是不满足的,不会执行下面的操作,
        继续等待。这种信号打断的方式也叫作假唤醒。
            
        pthread_mutex_lock();
        pthread_cond_signal();  // 如果没有线程等待,也就是说条件一直满足,那么信号将被丢弃。
            eg:假设条件是大于0,那么一开始大小为4时,满足条件,信号就不需要激活条件,执行完一次操作后,变成3仍然满足条件,信号
            也会被丢弃。
        pthread_mutex_unlock();
        
    生产者-消费者模型
            
            int *p = malloc(sizeof(int));
            *p = i;
            pthread_create(&tid[i], NULL, producer, p);
            
        在线程中切记要释放掉开辟的堆内存
            int id = *(int *)arg;
            free(arg);
    
信号量    #include

    1.定义信号量 sem_t sem;
    2.初始化信号量 int sem_init(sem_t *sem, int pshared, unsigned int value);
                a. sem地址    b. 0表示本进程中多个线程间同步,非0表示可以跨进程的同步操作。c. 信号量初值(计数器的值)
    3.PV操作
        P   int sem_wait(sem_t *sem);   // sem-1 如果小于0就阻塞
        V    int sem_post(sem_t *sem);   // sem+1
    4.销毁 int sem_destroy(sem_t *sem);
    
    仓库存放东西有限
        生产者
        
        while (1)
        {
            pthread_mutex_lock();
            sem_wait(sem_full);
            生产
            sem_post(sem_empty);
            pthread_mutex_unlock();
        }
        
        消费者
        
        while (1)
        {
            pthread_mutex_lock();
            sem_wait(sem_empty);
            消费
            sem_post(sem_full);
            pthread_mutex_unlock();
        }
        
    场景:http服务器是基于短连接的,那么在网页上的访问就会频繁地创建和销毁线程,这样开销是很大的。
线程池(生产者消费者模型)
    根据系统性能创建合适大小的线程池。
    将创建的线程存入在线程池中,需要线程时,就让它们建立联系,不想用了,就断开联系。
    
        1.线程池中有若干个线程
        2.用于执行大量相对短暂的任务
    
    计算密集型任务:大量的时间占用CPU进行运算。   
        线程池中线程的个数等于CPU个数,避免线程切换。
    IO密集型任务:大量的时间占用CPU在阻塞等待IO。    
        线程池中线程个数大于CPU个数。
    
    当任务增加时,可以动态增加线程池中线程的个数。当任务执行完成后,可以动态减少线程池中线程个数。
    
    需求:
        生产者线程向任务队列中添加任务,任务队列中有任务,如果线程池中有等待线程就唤醒它并执行任务,如果线程池中没有
    等待线程,并且没有达到上限,就添加新的线程到线程池。
        没有任务执行就等待,有任务则去执行。
    
        typedef struct condition
        {
            pthread_mutex_t mutex;
            pthread_cond_t cond;
        }condition_t;
        
        // 任务队列
        typedef struct task
        {
            void *(pfun)(void *); // 任务队列的回调函数(管理任务的)
            void *arg; // 回调函数传入的参数
            struct task *next;
        }task_t;
        
        typedef struct threadpool
        {
            condition_t cond; // 同步与互斥
            task_t *first; // 任务队列的队头
            task_t *tail; // 任务队列的队尾
            int max_thread; // 最大线程个数
            int idle; // 空闲线程个数   如果有空闲线程,此时可以signal通知下
            int counter; // 当前线程池中的线程个数
            int quit;    // 如果为1表示退出,为0表示不退出
        }threadpool_t;
    
        // 初始化
        void threadpool_init(threadpool_t *pool, int max);
        // 往线程池添加任务
        void threadpool_add(threadpool_t *pool, void*(*pf)(void*), void *arg);
        // 销毁线程池
        void threadpool_destroy(threadpool_t *pool);
        
        
        pthread_cond_broadcast();   // 唤醒所有
        pthread_cond_signal();   // 只唤醒一个
   

你可能感兴趣的:(Linux)