Linux 下 线程

文章详细阐述了线程的创建、终止和线程安全,包括最佳线程数的计算、线程的join和detach、互斥锁和条件变量的应用。还讨论了死锁的四个必要条件以及如何预防死锁,提到了线程池和读写锁的概念,强调了在多线程环境下资源管理和同步的重要性。
摘要由CSDN通过智能技术生成

 线程与进程

 

 

对于 I/O 密集型计算场景,最佳的线程数是与程序中 CPU 计算和 I/O 操作的耗时比相关的,总结一个公式
最佳线程数 =1 +(I/O 耗时 / CPU 耗时)

不过上面这个公式是针对单核 CPU 的,至于多核 CPU 需要等比扩大,计算公式如下:

最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]

创建一个线程 

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* mythread_start(void* arg){
    while(1){
        printf("i am work thread\n");
        sleep(1);
    }
}

int main(){
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, mythread_start, NULL);
    if(ret < 0){
        perror("pthread_create");
        return 0;
    }

    while(1){
        printf("i am main thread\n");
        sleep(1);
    }

    return 0;
}

 

线程终止

void pthread_exit(void *retval);

retval :线程退出时,传递给等待线程的退出信息。

作用: 谁调用谁退出

int pthread_cancel(pthread_t thread)

退出某个线程

thread:被终止的线程的标识符

线程等待

1. 线程被创建出来的默认属性是joinable属性,(退出的时候,依赖其他线程来回收资源(主要是退出线程使用到的共享区当中的空间)

int pthread_join(pthread_t thread,void **retval)//(等待的线程调用)

thread: 线程的标识符

retval:退出线程的退出信息


第一种:线程入口函数代码执行完毕,线程退出的, 就是入口函数的返回值
第二种:pthread_exit退出的,就是pthread_exit的参数
第三种:pthread_cancel退出的, 就是一个宏 : PTHREAD CANCELED

线程分离

设置线程的分离属性,一旦线程设置了分离属性,则线程退出的时候, 不需要任何人回收资源。操作系统可以进行可收

int pthread_detach(pthread_t thread) :

thread: 设置线程分离的线程的标识符

黄牛抢票(线程间的竞争)

 #include<stdio.h>
     #include<unistd.h>
     #include<pthread.h>
     int g_tickets=100;
 void* mythread_start(void* arg)
     {
     while(1)
     {
       if(g_tickets<=0)
        break;
   printf("i am %p_____ i get ticket%d\n",pthread_self(),g_tickets--);
    }
 }
    int main()
    {
      pthread_t tid[2];
      int i;
    for( i=0;i<2;++i)
     {
     int ret= pthread_create(&tid[i],NULL,mythread_start,NULL);
      if(ret<0)
      {
        perror("pthread_create");
        return 0;
     }                                                                                                                                                
     }
     for(i=0;i<2;++i)
    {
      pthread_join(tid[i],NULL);//阻塞调用
     }
     return 0;
    }

线程安全

 

线程在usleep(100)的时候别的线程工作抢了票//线程1抢了票,票还没减减,线程2就抢了票】



//线程不安全

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
int g_tickets=100;
     pthread_mutex_t g_lock; void* mythread_start(void* arg)
     {
     while(1)
     {
      pthread_mutex_lock(&g_lock);//加锁

      if(g_tickets<=0)
      {
       pthread_mutex_unlock(&g_lock);//开锁
      break;
      }
    
   printf("i am %p_____ i get ticket%d\n",pthread_self(),g_tickets);
      g_tickets--;
      pthread_mutex_unlock(&g_lock);//开锁//保持临界资源的操作原子性
      sleep(1);
    }
 }
    int main()
    {
      pthread_t tid[2];
      int i;
     for( i=0;i<2;++i)
    {
      int ret= pthread_create(&tid[i],NULL,mythread_start,NULL);                                                                                                                                                
      if(ret<0)
      {
        perror("pthread_create");
       return 0;
      }
     }
     for(i=0;i<2;++i)
     {
       pthread_join(tid[i],NULL);
     }
     pthread_mutex_destroy(&g_lock);
     return 0;
    }

条件变量 

 

 

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

/*
 * 0:  没面
 * 1: 有面
 * */
int g_bowl = 0;//临界资源
pthread_mutex_t g_mutex; 
pthread_cond_t g_cond_eat;//条件变量类型
pthread_cond_t g_cond_make;//

void *eat_start(void* arg){
    pthread_detach(pthread_self());
    while(1){
        pthread_mutex_lock(&g_mutex);
        while(g_bowl <= 0){
            pthread_cond_wait(&g_cond_eat, &g_mutex);//第二个参数用来解锁,因为你要进入pcb等待队列了
        }
        printf("i am eat start, i eat %d\n", g_bowl);
        g_bowl--;
        pthread_mutex_unlock(&g_mutex);
        pthread_cond_signal(&g_cond_make);//唤醒等待队列线程
    }
    return NULL;
}

void* make_start(void* arg){
    pthread_detach(pthread_self());//自己回收自己退出状态信息
    while(1){
        pthread_mutex_lock(&g_mutex);
        while(g_bowl > 0){
            /*
             * 1
             * 2
             * */
            pthread_cond_wait(&g_cond_make, &g_mutex);
        }
        g_bowl++;
        printf("i am make start, i make %d\n", g_bowl);
        pthread_mutex_unlock(&g_mutex);

        pthread_cond_signal(&g_cond_eat);
    }
    return NULL;
}

int main(){
    /*
     * 1.创建吃面线程和做面线程
     * */
    pthread_mutex_init(&g_mutex, NULL);
    pthread_cond_init(&g_cond_eat, NULL);
    pthread_cond_init(&g_cond_make, NULL);
    pthread_t tid;
    for(int i = 0; i < 3; i ++){
        int ret = pthread_create(&tid, NULL, eat_start, NULL);
        if(ret < 0){
            perror("pthread_create");
            return 0;
        }
        ret = pthread_create(&tid, NULL, make_start, NULL);
        if(ret < 0){
            perror("pthread_create");
            return 0;
        }
    }

    while(1){
        sleep(1);
    }
    pthread_mutex_destroy(&g_mutex);
    pthread_cond_destroy(&g_cond_eat);
    pthread_cond_destroy(&g_cond_make);
    return 0;
}

 死锁

死锁情况1

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

pthread_mutex_t mutex;

void* mythread_strat(void* arg){
    int count = 10;
    while(1){
        pthread_mutex_lock(&mutex);
        if(count < 0){
            break;
        }
        count--;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main(){
    pthread_t tid;

    for(int i = 0; i < 2; i++){
        int ret = pthread_create(&tid, NULL, mythread_strat, NULL);
        if(ret < 0){
            perror("pthread_create");
            return 0;
        }
    }
    pthread_mutex_init(&mutex, NULL);

    printf("mutex...\n");

    while(1){
        sleep(1);
    }
    return 0;
}



int main(){
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);
    pthread_mutex_lock(&mutex);
    pthread_mutex_lock(&mutex);

    printf("mutex...\n"); 
    return 0;
}

死锁情况2

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;

void* mythread_stratA(void* arg){
    pthread_mutex_lock(&mutex1);
    sleep(1); //让线程B将2锁拿走
    pthread_mutex_lock(&mutex2);

    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
}

void* mythread_stratB(void* arg){
    pthread_mutex_lock(&mutex2);
    sleep(1); //让线程A将1锁拿走
    pthread_mutex_lock(&mutex1);


    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex2);
}

int main(){
    pthread_mutex_init(&mutex1, NULL);
    pthread_mutex_init(&mutex2, NULL);
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, mythread_stratA, NULL);
    if(ret < 0){
        perror("pthread_create");
        return 0;
    }

    ret = pthread_create(&tid, NULL, mythread_stratB, NULL);
    if(ret < 0){
        perror("pthread_create");
        return 0;
    }

    while(1){
        sleep(1);
    }
    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);
    return 0;
}

死锁产生的必要条件

1. 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。

2. 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。

3. 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。

4. 环路等待条件:在发生死锁时,必然存在一个进程–资源的环形链。



4个必要条件当中:

不可剥夺 和 互斥条件是互斥锁的基础属性,程序员是没有办法通过代码改变的。

程序员可以通过代码的手段, 破坏循环等待或者请求与保持条件

怎样预防死锁

破坏必要条件   :(循环等待)/(请求保持)

避免锁未释放   : 在所有线程可能退出的地方释放锁

资源一次性分配 : 多个资源在代码当中又可能每一个资源都需要使用不同锁保护//(全局变量A && 全局变量B

这两种资源我们用一种锁保护)

消费者&&生产者模型

1. 一个线程安全队列  (同步)(互斥)

2. 两种角色线程  (消费者) (生产者)

3. 生产者与消费者 互斥

   消费者与消费者 互斥

   生产者与消费者 互斥 + 同步

队列

起到了生产者和消费者缓冲的作用

生产者不用因为无人消费发愁,只需将生产数据放在队列中

消费者不用为生产者生产了大量数据而发愁,只需要关注正在处理的正常数据即可

优点:生产者与消费者解耦

  忙闲不均

支持高并发

代码实现

1. 实现线程安全队列
    队列: std :: queue
   
   线程安全:
       互斥:pthread_mutex_t

       同步:pthread_cond_t


  两种角色的线程
  pthread_create
class SafeQueue{
    public:
        SafeQueue(){
            pthread_mutex_init(&que_lock_, NULL);
            pthread_cond_init(&cons_cond_, NULL);
            pthread_cond_init(&prod_cond_, NULL);
            capacity_ = 1;
        }

        ~SafeQueue(){
            pthread_mutex_destroy(&que_lock_);
            pthread_cond_destroy(&cons_cond_);
            pthread_cond_destroy(&prod_cond_);
        }

        /*
         * 插入接口-生产者调用的
         * */
        void Push(int data){
            pthread_mutex_lock(&que_lock_);
            while(que_.size() >= capacity_){
                pthread_cond_wait(&prod_cond_, &que_lock_);
            }

            que_.push(data);
            printf("i am product, i product %d\n", data);
            pthread_mutex_unlock(&que_lock_);

            /*通知消费者进行消费*/
            pthread_cond_signal(&cons_cond_);
        }

        /*
         * 获取元素的接口-消费者调用的
         * */
        int Pop(){
            pthread_mutex_lock(&que_lock_);
            while(que_.empty()){
                pthread_cond_wait(&cons_cond_, &que_lock_);
            }
            int tmp = que_.front();
            que_.pop();
            printf("i am consume, i consume %d\n", tmp);
            pthread_mutex_unlock(&que_lock_);

            /*通知生产者进行生产*/
            pthread_cond_signal(&prod_cond_);
            return tmp;
        }
    private:
        /* stl当中的queue是线程不安全的, 所以需要进行保护 */
        std::queue<int> que_;
        /* 人为约定队列的大小 */
        size_t capacity_;
        pthread_mutex_t que_lock_;
        /*消费者的条件变量*/
        pthread_cond_t cons_cond_;
        /*生产者的条件变量*/
        pthread_cond_t prod_cond_;

};
#include "queuesafe.hpp"

#define THREADCOUNT 1

void* cons_start(void* arg){
    SafeQueue* sq = (SafeQueue*)arg;

    while(1){
        int ret = sq->Pop();
        //printf("i am consume, i consume %d\n", ret);
        if(ret == 999){
            break;
        }
    }
    return NULL;
}

void* prod_start(void* arg){
    /*
     * 获取线程安全的队列
     * */
    SafeQueue* sq = (SafeQueue*)arg;

    int data = 0;
    while(1){
        if(data >= 1000){
            break;
        }
        sq->Push(data);
        //printf("i am product, i product %d\n", data);
        data++;
    }
    return NULL;
}

int main(){
    SafeQueue* sq = new SafeQueue();
    if(sq == NULL){
        perror("new");
        return 0;
    }

    pthread_t cons[THREADCOUNT], prod[THREADCOUNT];
    for(int i = 0; i < THREADCOUNT; i++){
        int ret = pthread_create(&cons[i], NULL, cons_start, (void*)sq);
        if(ret < 0){
            perror("pthread_create");
            return 0;
        }

        ret = pthread_create(&prod[i], NULL, prod_start, (void*)sq);
        if(ret < 0){
            perror("pthread_create");
            return 0;
        }
    }

    /*主线程进行线程等待 - 阻塞等待*/
    for(int i = 0; i < THREADCOUNT; i++){
        pthread_join(cons[i], NULL);
        pthread_join(prod[i], NULL);
    }
    return 0;
}

信号量

信号量原理

资源计数器(描述资源可用情况)+PCB等待队列(当资源不可用时,将线程放到PCB等待队列进行等待)

资源计数器:执行流获取信号,获取成功,信号量计数器减1操作
                          获取失败,执行流放入到PCB等待队列

信号量接口


int sem_init(sem_t *sem, int pshared,unsigned int value);
    sem : 信号量, sem_t是信号量的类型
    pshared : 该信号量是用于线程间还是用于进程间
    0 : 用于线程间,全局变量
    非0  : 用于进程间


将信号量所用到的资源在共享内存当中进行开辟

value : 资源的个数, 初始化信号量计数器的

等待接口

int sem_wait(sem_t *sem);

1.对资源计数器进行减1操作
2.判断资源计数器的值是否小于0

是:则阻塞等待,将执行流放到PCB等待队列当中

不是:则接口返回

释放接口

int sem_post(sem_t *sem);

1.会对资源计数器进行加1
2.判断资源计数器的值是否小于等于0


是: 通知PCB等待队列
否:不用通知PCB等待队列, 因为没有线程在等待

销毁接口

int sem_destroy(sem_t *sem);
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>

int g_tickets = 100;
sem_t g_lock;

void* thread_startA(void* arg){
    pthread_detach(pthread_self());
    while(1){
        sem_wait(&g_lock); //“加锁操作”
        if(g_tickets <= 0){
            sem_post(&g_lock); //"解锁操作"
            break;
        }
        printf("i am thread_startA %p, i have %d ticket\n", pthread_self(), g_tickets);
        g_tickets--;
        sem_post(&g_lock); //"解锁操作"
        sleep(1);
    }
}

void* thread_startB(void* arg){
    pthread_detach(pthread_self());
    while(1){
        sem_wait(&g_lock); //“加锁操作”
        if(g_tickets <= 0){
            sem_post(&g_lock); //"解锁操作"
            break;
        }
        printf("i am thread_startB %p, i have %d ticket\n", pthread_self(), g_tickets);
        g_tickets--;
        sem_post(&g_lock); //"解锁操作"
        sleep(1);
    }
}

int main(){
    sem_init(&g_lock, 0, 1);

    pthread_t tid;
    int ret = pthread_create(&tid, NULL, thread_startA, NULL);
    if(ret < 0){
        perror("pthread_create");
        return 0;
    }
    ret = pthread_create(&tid, NULL, thread_startB, NULL);
    if(ret < 0){
        perror("pthread_create");
        return 0;
    }

    while(1){
        sleep(1);
    }
    sem_destroy(&g_lock);
    return 0;
}

线程池

1.应用场景

多线程程序是为了解决程序运行效率的问题

单线程的代码:一定是串行化运行的

线程池(创建多个线程)不仅要能够提高程序运行效率,还要提高程序处理业务种类

业务种类较少1.(switch case  ,ifelse)但是种类一旦多起来的话分支语句就不适用了

代码如何去实现呢?
     1. 线程安全的队列
     线程安全 :互斥+同步  ,信号量


元素类型:
        数据
        处理数据的函数(任务接口)
    队列:
        STI.queue



2.线程池的定义:

          线程安全的队列
          一堆的线程
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <queue>

/*
 * 定义队列元素的类型
 *    数据
 *    处理数据的方法
 * */

typedef void (*Handler)(int data);

class QueueData{
    public:
        QueueData(){

        }

        QueueData(int data, Handler handler){
            data_ = data;
            handler_ = handler;
        }

        /*
         * 怎么通过函数处理数据
         * */
        void Run(){
            handler_(data_);
        }
    private:
        //要处理的数据
        int data_;
        //要处理数据的函数(要保存一个函数的地址)
        //Handler 函数指针, handler 函数指针变量, 保存函数的地址
        Handler handler_;
};


/*
 * 线程队列
 *   互斥+同步
 *   元素类型
 *   队列
 * */

class SafeQueue{
    public:
        SafeQueue(){
            capacity_ = 1;
            pthread_mutex_init(&lock_, NULL);
            pthread_cond_init(&prod_cond_, NULL);
            pthread_cond_init(&cons_cond_, NULL);
        }

        ~SafeQueue(){
            pthread_mutex_destroy(&lock_);
            pthread_cond_destroy(&prod_cond_);
            pthread_cond_destroy(&cons_cond_);
        }

        void Push(QueueData& data){
            pthread_mutex_lock(&lock_);
            while(que_.size() >= capacity_){
                pthread_cond_wait(&prod_cond_, &lock_);
            }
            que_.push(data);
            pthread_mutex_unlock(&lock_);

            pthread_cond_signal(&cons_cond_);
        }

        /*
         * data : 出参, 返回给调用者的
         * */
        void Pop(QueueData* data, int flag_exit){
            pthread_mutex_lock(&lock_);
            while(que_.empty()){
                if(flag_exit == 1){
                    pthread_mutex_unlock(&lock_);
                    pthread_exit(NULL);
                }
                pthread_cond_wait(&cons_cond_, &lock_);
            }
            *data = que_.front();
            que_.pop();
            pthread_mutex_unlock(&lock_);

            pthread_cond_signal(&prod_cond_);
        }

        void BroadcaseAllConsume(){
            pthread_cond_broadcast(&cons_cond_);
        }
    private:
        std::queue<QueueData> que_;
        size_t capacity_;
        /* 互斥锁 */
        pthread_mutex_t lock_;

        /*
         * 同步 :
         *   生产者的条件变量()
         *   消费者的条件变量(线程池当中的一堆线程, 在逻辑上就是消费线程)
         * */
        pthread_cond_t prod_cond_;
        pthread_cond_t cons_cond_;
};


class ThreadPool{
    public:
        ThreadPool(){
        }

        ~ThreadPool(){
            if(sq_ != NULL){
                delete sq_;
            }
        }

        int InitThreadPool(int thread_count){
            /*
             * 0 : 线程继续运行
             * 1 : 线程退出
             * */
            flag_exit_ = 0;

            sq_ = new SafeQueue;
            if(sq_ == NULL){
                printf("Init thread pool failed\n");
                return -1;
            }

            thread_count_ = thread_count;

            /*
             * thread_count : 10
             * */
            for(int i = 0; i < thread_count_; i++){
                pthread_t tid;
                int ret = pthread_create(&tid, NULL, worker_start, (void*)this);
                if(ret < 0){
                    thread_count--;
                    continue;
                }
            }

            /* 
             * 判断一下线程的数量
             *    thread_count_ <= 0 : 创建工作线程全部失败, 程序就返回负数,告诉给调用者
             *    thread_count_ > 0 : 说明线程池初始化成功了
             * */
            if(thread_count_ <= 0){
                printf("create thread all failed\n");
                return -1;
            }
            //代表初始化成功
            return 0;
        }

        /* 
         * 线程池的使用接口 
         *   只需要给使用者提供push接口,让他能够将数据push到队列当中就好
         *   而 pop接口不需要提供, 因为线程池当中的线程可以自己调用到
         * */
        void Push(QueueData& qd){
            sq_->Push(qd);
        }

        /* 线程入口函数 */
        static void* worker_start(void* arg)//为什么是static因为函数声明在类中默认传递this指针
        {
            pthread_detach(pthread_self());
            /*
             * 1. 从队列当中拿元素
             * 2. 处理元素
             * */
            ThreadPool* tp = (ThreadPool*)arg;
            while(1){
                QueueData qd;
                tp->sq_->Pop(&qd, tp->flag_exit_);
                
                qd.Run();
            }
        }

        void thread_pool_exit(){
            flag_exit_ = 1;
            sq_->BroadcaseAllConsume();
        }
    private:
        /* 线程安全的队列 */
        SafeQueue* sq_;
        /* 线程池当中线程的数量 */
        int thread_count_;
        /* 标志线程是否退出的标志位 */
        int flag_exit_;
};



void Deal1(int data){
    printf("i am Deal1, i deal %d\n", data);
}

void Deal2(int data){
    printf("hhhhhh, i am Deal2, deal %d\n", data);
}

int main(){
    /*
     * 1. create thread pool
     *
     * 2. push data to thread pool
     * */

    ThreadPool tp;
    int ret = tp.InitThreadPool(2);
    if(ret < 0){
        return 0;
    }

    for(int i = 0; i < 100; i++){
        QueueData qd(i, Deal1);
        tp.Push(qd);
    }

    tp.thread_pool_exit();

    while(1){
        sleep(1);
    }
    return 0;
}

线程池中的线程应当如何退出

不应该出现的//主线程退出导致进程退出

线程以优雅的姿态退出

1.线程主动退出( 线程收到某些指令pthread_exit() ),而并非因为进程退出,被迫销毁

2.队列中没有待要处理元素(线程已经将活儿干完了,不存在没干完活儿就退出了)

// 线程池中增加标志位 1:线程退出 0.线程继续运行(使用其他线程来修改这个值)

在每次pop(处理业务)时进行判定标志位//如果业务做完了,且我们想让这个线程退出,我们让他退出,推出前让它把锁释放掉

 读写锁

读写锁:
      模式 :


   以读模式加锁以写模式加锁

读写锁适用的场景一定是,大量读,少量写的情况因为读写锁允许多个线程并行的读, 多个线程是互斥写的(读-写, 写-写)

多个线程,访问临界资源的时候,都是读临界资源的内容, 一定不会产生二义性结果

初始化:

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
                                 const pthread_rwlockattr_t *restrict attr);

1.读写锁的类型: pthread_rwlock_t
2. attr : NULL,采用默认属性

销毁:
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

加锁:

读模式加锁(读模式可以共享)int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);-- 阳塞加锁的接

写模式加锁(相当于互斥锁)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

解锁:
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

--------------------------------------------------------------------------------
读--读(并发)

读--写(互斥)
写--读(互斥)
写--写(互斥)
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

pthread_rwlock_t  rwl;
void* Thread_start1(void* arg){
    pthread_rwlock_wrlock(&rwl);
    printf("线程1 写模式加锁成功...\n");

    sleep(10000);
    return NULL;
}

void* Thread_start2(void* arg){
    sleep(2);

    pthread_rwlock_rdlock(&rwl);
    printf("线程2 读模式加锁成功...\n");


    sleep(10000);
    return NULL;
}

int main(){
    /*
     * 读模式 + 读模式
     * */
    for(int i = 0; i < 1; i++){
        pthread_t  tid;
        pthread_create(&tid, NULL, Thread_start1, NULL);
        pthread_create(&tid, NULL, Thread_start2, NULL);
    }
    pthread_rwlock_init(&rwl, NULL);

    while(1){
        sleep(1);
    }

    pthread_rwlock_destroy(&rwl);
    return 0;
}

 多个读进程多个写进程

引用计数: 用来记录当前读写锁有多少个线程以读模式获取了读写锁

1. 当有线程以读模式进行加锁, 加锁成功, 则引用计数++

2.当以读模式打开读写锁的线程, 释放了读写锁之后,引用计数--

3.只有当引用计数为0的时候, 线程才可以以写模式打开读写锁

4. 如果读写锁已经以读模式打开了, 有一个线程A想要以写模式打开获取读写锁,
则需要等待如果在等待期间, 又来了读模式加锁的线程,
 拿读模式的线程也跟着等待(避免造成写线程饥饿)

单例模式

9.3 单例模式:

1、单例类只能有一个实例。

2.单例类必须自己创建自己的唯一实例。

3、单例类必须给所有其他对象提供这一实例。

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
判断系统是否已经有这个单例,如果有则返回,如果没有则创建如何解决:
关键代码:构造函数是私有的
单例模式两种形式 (懒汉 & 饿汉)

饿汉模式: 在程序启动的时候就创建唯一的实例对象,饿汉模式不需要加锁

懒汉模式: 当你第一次使用时才创建一个唯一的实例对象,从而实现延迟加载效果。
懒汉模式在第一次使用单例对象时才完成初始化工作,因此可能存在多线程竞态环境,
如果不加锁会导致重复构造或构造不完全问题。
#include <iostream>

/* 懒汉模式的代码实现
 * 懒汉模式的单例类
 *    并不是程序启动的时候, 就实例化对象
 *    而是用到的时候, 才进行实例化对象
 * */

class sigleton{
    public:
        static sigleton* GetInstance();
        void Print(){
            std::cout << "sigleton print" << std::endl;
        }
    private:
        sigleton(){};
        static sigleton* st;
        static pthread_mutex_t lock_;
};

pthread_mutex_t sigleton::lock_ = PTHREAD_MUTEX_INITIALIZER;
sigleton* sigleton::st = NULL;
sigleton* sigleton::GetInstance(){
    if(st == NULL)//这里if判断是否已经存在实例化对象如果有直接返回避免加锁提高效率
{
        pthread_mutex_lock(&sigleton::lock_);
        if(st == NULL){
            st = new sigleton;
        }
        pthread_mutex_unlock(&sigleton::lock_);
    }
    return st;
}

int main(){
    sigleton* st = sigleton::GetInstance();
    st->Print();
    std::cout << st << std::endl;


    sigleton* st1 = sigleton::GetInstance();
    st1->Print();
    std::cout << st1 << std::endl;

    return 0;
}

乐观锁&&悲观锁

乐观锁 && 悲观锁
   
   悲观锁::针对某个线程访问临界区修改数据的时候,都会认为可能有其他线程并行修改的情况发生,
   所以在线程修改数据之前就进行加锁
   
   悲观锁:针对某个线程访问临界区修改数据的时候,让多个线程互斥访问。
   
   悲观锁有: 互斥锁,读写锁,自旋锁等等

   自旋锁 (busy-waiting类型) 和互斥锁 (sleep-waiting类型) 的区别:1.自旋锁加锁时,加不到锁,
线程不会切换(时间片没有到的情况, 时间片到了, 也会线程切换),会持续的尝试拿锁,
 直到拿到自旋锁

   互斥锁加锁时,加不到锁,切换回来,进行加锁
线程会切换(时间片没有到, 也会切换), 进入睡眠状态,当其他线程释放互斥锁(解锁) 之后,
被唤醒。在切换回来抢锁

   自旋锁的优点:因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远高于互斥锁。

   自旋锁的缺点:自旋锁一直占用着CPU,他在未获得锁的情况下,一直运行(自旋),
   所以占用着CPL,如果不能在很短的时间内获得锁,这无疑降低了cpu效率,适用于临界区代码较短时
   (直白的说: 临界区代码执行时间短)的情况, 使用自旋锁效率比较高。因为线程不用来回切换。

   
   当临界区当中执行时间较长,自旋锁就不适用了,因为拿不到锁会占用CPU一直抢占锁。



------------------------------------------------------------------------------------------

   乐观锁:乐观的认为只有该线程在修改, 大概率不会存在并行的情况。所以修改数据不加锁,进行判断但是,在修改完毕, 进行更新的时候,例如:(版本号控制, CAS无锁编程

#include <pthread.h>

int pthread_spin_init(pthread_spinlock_t *lock, int pshared);

int pthread_spin_destroy(pthread_spinlock_t *lock);

int pthread_spin_lock(pthread_spinlock_t *lock);//阻塞

int pthread_spin_trylock(pthread_spinlock_t *lock);//非阻塞

int pthread_spin_unlock(pthread_spinlock_t *lock); 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值