zoukankan      html  css  js  c++  java
  • 线程的互斥锁

    一、竞争与同步
    当多个线程同时访问其所共享的进程资源时,需要相互协调,以防止出现数据不一致、不完整的问题。这就叫线程同步。

    二、互斥量

    int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
    功能:初始化互斥量
    //亦可 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

    int pthread_mutex_lock (pthread_mutex_t* mutex);
    功能:加锁

    int pthread_mutex_unlock (pthread_mutex_t* mutex);
    功能:解锁

    int pthread_mutex_destroy (pthread_mutex_t* mutex);
    功能:销毁互斥量

    1) 互斥量被初始化为非锁定状态;
    2) 线程1调用pthread_mutex_lock函数,立即返回,互斥量呈锁定状态;
    3) 线程2调用pthread_mutex_lock函数,阻塞等待;
    4) 线程1调用pthread_mutex_unlock函数,互斥量呈非锁定状态;
    5) 线程2被唤醒,从pthread_mutex_lock函数中返回,互斥量呈锁定状态;

    三、信号量
    信号量是一个计数器,用于控制访问有限共享资源的线程数。
    注意:线程使用的信号量不在pthread.h中,而是semaphore.h

    // 创建信号量
    int sem_init (sem_t* sem, int pshared,unsigned int value);

     sem           信号量ID,输出。
    ​pshared   一般取0,表示调用进程的信号量。非0表示该信号量可以共享内存的方式,为多个进程所共享(Linux暂不支持)。
     value    信号量初值。

    // 信号量减1,不够减即阻塞
    int sem_wait (sem_t* sem);

    // 信号量减1,不够减即返回-1,errno为EAGAIN
    int sem_trywait (sem_t* sem);

    // 信号量减1,不够减即阻塞,直到abs_timeout超时返回-1,errno为ETIMEDOUT

    int sem_timedwait (sem_t* sem,const struct timespec* abs_timeout);
    struct timespec {
        time_t tv_sec;  // Seconds
        long   tv_nsec; // Nanoseconds [0 - 999999999]
    };

    // 信号量加1
    int sem_post (sem_t* sem);

    // 销毁信号量
    int sem_destroy (sem_t* sem);

    //简单的图书借阅小程序
    #include <pthread.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <semaphore.h>
    #include <stdlib.h>
    #include <time.h>
    
    sem_t sem;
    void* fun(void* arg)
    {
        pthread_t tid = pthread_self();
        int* sec = arg;
        printf("请等待(%lu)...
    ",tid);
        sem_wait(&sem);
        printf("借阅成功(%lu),正在读书,读书时间为:%d秒
    ",tid,*sec);
        sleep(*sec);
        sem_post(&sem);
        printf("还书成功(%lu)!
    ",tid);
    
    }
    
    int main()
    {
        srand((int)time(NULL));
        pthread_t thread;
        sem_init(&sem,0,5);
    
        for(int i=0; i<20; i++)
        {
            int time = rand()%10;
            int ret = pthread_create(&thread,NULL,fun,&time);
            if(ret < 0)
            {
                perror("pthread_create");
                return -1;
            }
            sleep(1);
        }
        
        sem_destroy(&sem);
        while(1);        //主线程不结束
        
    }

    四、生产者与消费者模型
    一线程负责生产数据,另一部分线程负责消费数据。
    问题1:如何生产的快、消费的慢,生产者容易撑死
    问题2:如果生产的慢、消费的快,消费者容易饿死
    只有把问题1、和问题2协调好,才能最大限度的提高效率。

    生产者快->数据池满->生产者暂停->消费者全部开始消费->数据池空->消费者暂停->生产者全部开始生产

    五、条件变量
    条件变量可以让调用线程在满足特定条件的情况下暂停。

    //初始化条件变量
    int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr);
    //亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

    // 使调用线程睡入条件变量cond,同时释放互斥锁mutex
    int pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex);

    // 使调用线程睡入条件变量cond,同时释放互斥锁mutex,并在时间到了之后即使没有被唤醒,也醒过来
    int pthread_cond_timedwait (pthread_cond_t* cond,
        pthread_mutex_t* mutex,
        const struct timespec* abstime);

    struct timespec {
        time_t tv_sec;  // Seconds
        long   tv_nsec; // Nanoseconds [0 - 999999999]
    };

    // 从条件变量cond中唤出一个线程,令其重新获得原先的互斥锁
    int pthread_cond_signal (pthread_cond_t* cond);
    注意:被唤出的线程此刻将从pthread_cond_wait函数中返回,
    但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。

    // 从条件变量cond中唤出所有线程
    int pthread_cond_broadcast (pthread_cond_t* cond);

    // 销毁条件变量
    int pthread_cond_destroy (pthread_cond_t* cond);

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <unistd.h>
    
    #define MAX 20
    
    char storage[MAX] = {};
    int count = 0;
    
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t full = PTHREAD_COND_INITIALIZER;
    pthread_cond_t empty = PTHREAD_COND_INITIALIZER;
    
    void show_storage(char* role,char* op,char prod)
    {
        printf("%s:",role);
        for(int i=0; i<count; i++)
        {
            printf("%c",storage[i]);
        }
        printf("%s%c
    ",op,prod);
    }
    
    void* pro_run(void* arg)
    {
        char* who = "生产者";
        while(1)
        {
            pthread_mutex_lock(&mutex);
            while(count >= MAX)
            {
                printf("%s:满仓
    ",who);
                pthread_cond_wait(&full,&mutex);
            }
    
            char prod = 'A'+rand()%26;
            storage[count++] = prod;
    
            show_storage(who,"->",prod);
            
            pthread_cond_signal(&empty);
            pthread_mutex_unlock(&mutex);
            
            usleep(rand()%100*1000);
        }
    }
    
    void* con_run(void* arg)
    {
        char* who = "消费者";
        while(1)
        {
            pthread_mutex_lock(&mutex);
            while(count <= 0)
            {
                printf("%s:空仓
    ",who);
                pthread_cond_wait(&empty,&mutex);
            }
    
            char prod = storage[count--];
    
            show_storage(who,"<-",prod);
            
            pthread_cond_signal(&full);
            pthread_mutex_unlock(&mutex);
            
            usleep(rand()%100*1000);
        }
    }
    
    int main()
    {
        pthread_t tid1,tid2;
        pthread_create(&tid1,NULL,pro_run,NULL);
        pthread_create(&tid2,NULL,con_run,NULL);
        getchar();
    }
  • 相关阅读:
    党报
    一个人只有敢于承担责任,才有可能被赋予更大的责任。做不
    勇于担当:好男人的三块责任田——
    关于担当
    领导干部要勇于担当
    福布斯专访阿里蔡崇信:马云的坚持和改变
    阿里股权
    ContentProvider
    搞笑段子
    报业
  • 原文地址:https://www.cnblogs.com/xiehuan-blog/p/9687984.html
Copyright © 2011-2022 走看看