【Linux】线程——线程池、线程池的实现、线程安全的线程池、单例模式的概 ...

打印 上一主题 下一主题

主题 529|帖子 529|积分 1587

Linux线程

7. 线程池

  线程池是一种多线程编程中的技术和概念。
  它是一种线程使用模式。是一组预先创建好的线程聚集,这些线程处于等待状态,随时预备担当任务并实行。

  
7.1 线程池介绍

为什么使用线程池
  线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发实行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
  
线程池的应用场景
  (1)必要大量的线程来完成任务,且完成任务的时间比力短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不显着了。因为Telnet会话时间比线程的创建时间大多了。
  (2)对性能要求苛刻的应用,比如要求服务器迅速相应客户请求。
  (3)担当突发性的大量请求,但不至于使服务器因此产生大量线程的应用。 突发性大量客户请求,在没有线程池环境下,将产生大量线程,固然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。
  
使用线程池的优点
  (1)提高性能:避免了频繁创建和销毁线程的开销,因为线程的创建和销毁是比力耗时的操作。
  (2)控制资源:可以限定线程的数量,防止过多的线程竞争系统资源,导致系统性能降落乃至崩溃。
  (3)提高相应性:能够更快地相应新的任务请求,因为线程已经预备好,无需等待线程创建。
  
7.2 线程池的实现

线程池示例
  (1)创建固定命量线程池,循环从任务队列中获取任务对象。
  (2)获取到任务对象后,实行任务对象中的任务接口。
  
实行任务:
  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. std::string opers="+-*/%";
  5. enum{
  6.     DivZero=1,
  7.     ModZero,
  8.     Unknown
  9. };
  10. class Task
  11. {
  12. public:
  13.     Task()
  14.     {}
  15.    
  16.     Task(int x,int y,char op)
  17.         :_data1(x),_data2(y),_oper(op),_result(0),_exitcode(0)
  18.     {}
  19.     void run()
  20.     {
  21.         switch (_oper)
  22.         {
  23.         case '+':
  24.             _result=_data1+_data2;
  25.             break;
  26.         case '-':
  27.             _result=_data1-_data2;
  28.             break;
  29.         case '*':
  30.             _result=_data1*_data2;
  31.             break;
  32.         case '/':
  33.             {
  34.                 if(_data2==0) _exitcode=DivZero;
  35.                 else _result=_data1/_data2;
  36.             }
  37.             break;
  38.         case '%':
  39.             {
  40.                 if(_data2==0) _exitcode=ModZero;
  41.                 else _result=_data1%_data2;
  42.             }
  43.             break;
  44.         default:
  45.             _exitcode=Unknown;
  46.             break;
  47.         }
  48.     }
  49.     //Task对象重载运算符(),()直接进行run函数
  50.     void operator()()
  51.     {
  52.         run();
  53.     }
  54.     std::string GetResult()
  55.     {
  56.         std::string r=std::to_string(_data1);
  57.         r+=_oper;
  58.         r+=std::to_string(_data2);
  59.         r+="=";
  60.         r+=std::to_string(_result);
  61.         r+="[code: ";
  62.         r+=std::to_string(_exitcode);
  63.         r+="]";
  64.         return r;
  65.     }
  66.     std::string GetTask()
  67.     {
  68.         std::string r=std::to_string(_data1);
  69.         r+=_oper;
  70.         r+=std::to_string(_data2);
  71.         r+="=?";
  72.         return r;
  73.     }
  74.     ~Task()
  75.     {}
  76. private:
  77.     int _data1;
  78.     int _data2;
  79.     char _oper;
  80.     int _result;
  81.     int _exitcode;
  82. };
复制代码
  
线程池:
  1. #pragma once
  2. #include <iostream>
  3. #include <vector>
  4. #include <queue>
  5. #include <string>
  6. #include <pthread.h>
  7. #include <unistd.h>
  8. #include "Task.hpp"
  9. struct ThreadData
  10. {
  11.     pthread_t tid;
  12.     std::string name;
  13. };
  14. static const int defaultnum=5; //默认线程数量
  15. //实现我们的线程池
  16. template<class T>
  17. class ThreadPool
  18. {
  19. public:
  20.     void Lock()
  21.     {
  22.         pthread_mutex_lock(&_mutex);
  23.     }
  24.     void Unlock()
  25.     {
  26.         pthread_mutex_unlock(&_mutex);
  27.     }
  28.     void Wakeup()
  29.     {
  30.         pthread_cond_signal(&_cond);
  31.     }
  32.     void ThreadSleep()
  33.     {
  34.         pthread_cond_wait(&_cond,&_mutex);
  35.     }
  36.     bool IsQueueEmpty()
  37.     {
  38.         return _tasks.empty();
  39.     }
  40. public:
  41.     //注意我们线程调用的函数要求参数和返回值都是void*
  42.     //但是handler在类中默认有this指针->参数不匹配,可以bind或者声明static或放在类外
  43.     static void *Handler(/*ThreadPool *this,*/void *args)
  44.     {
  45.         ThreadPool<T> *tp=static_cast<ThreadPool<T>*>(args);
  46.         while(true)
  47.         {
  48.             tp->Lock();
  49.             
  50.             while(tp->IsQueueEmpty()) //判断任务是否为空
  51.             {
  52.                 tp->ThreadSleep(); //条件变量
  53.             }
  54.             T t=tp->Pop(); //取出任务
  55.             tp->Unlock();
  56.             t(); //处理任务
  57.             std::cout<<" run, "<<"result: "<< t.GetResult()<<std::endl;            
  58.         }
  59.         return nullptr;
  60.     }
  61.     void Start() //启动线程池
  62.     {
  63.         int num=_threads.size();
  64.         for(int i=0;i<num;i++)
  65.         {
  66.             _threads[i].name="thread-"+std::to_string(i+1);
  67.             pthread_create(&(_threads[i].tid),nullptr,Handler,this);
  68.         }
  69.     }
  70.     void Push(const T &t) //向任务队列放入任务
  71.     {
  72.         Lock();
  73.         _tasks.push(t); //放入任务
  74.         Wakeup(); //唤醒线程
  75.         Unlock();
  76.     }
  77.     T Pop() //取出任务
  78.     {
  79.         T t=_tasks.front();
  80.         _tasks.pop();
  81.         return t;
  82.     }
  83.     ThreadPool(int num=defaultnum)
  84.         :_threads(num)
  85.     {
  86.         pthread_mutex_init(&_mutex,nullptr);
  87.         pthread_cond_init(&_cond,nullptr);
  88.     }
  89.     ~ThreadPool()
  90.     {
  91.         pthread_mutex_destroy(&_mutex);
  92.         pthread_cond_destroy(&_cond);
  93.     }
  94. private:   
  95.     std::vector<ThreadData> _threads; //线程池
  96.     std::queue<T> _tasks; //任务队列
  97.     pthread_mutex_t _mutex; //锁
  98.     pthread_cond_t _cond; //条件变量
  99. };
复制代码
  
运行函数:
  1. #include <iostream>
  2. #include "ThreadPool.hpp"
  3. int main()
  4. {
  5.     ThreadPool<Task> *tp=new ThreadPool<Task>(5);
  6.     tp->Start();
  7.     srand(time(nullptr) ^ getpid());   
  8.     while(true)
  9.     {
  10.         //1. 构建任务
  11.         int x=rand()%10+1;
  12.         usleep(10);
  13.         int y=rand()%5;
  14.         char op=opers[rand()%opers.size()];
  15.         Task t(x,y,op);
  16.         tp->Push(t);
  17.         //2. 交给线程池处理
  18.         std::cout<<"main thread make task: "<<t.GetTask()<<std::endl;
  19.         sleep(1);
  20.     }
  21.     return 0;
  22. }
复制代码
  

  
7.3 线程安全的线程池

7.3.1 单例模式的概念

  单例模式是一种常见的软件计划模式
  概念:单例模式确保一个类只有一个实例存在,并提供一个全局访问点来获取该实例。
  
特点包罗
  唯一性:保证一个类在整个应用程序中只有一个实例。
  全局访问:提供了一种全局访问这个唯一实例的方式,方便在程序的任何地方使用。
  延迟初始化:通常实例的创建是延迟的,即在首次使用时才创建实例,以提高性能和资源利用率。
  
单例模式的优点
  节流系统资源:避免了频繁创建和销毁对象带来的资源消耗。
  统一管理:对唯一的实例进行集中管理和控制,方便维护和修改。
  保证一致性:在整个应用中,对于共享的数据或状态,通过单例模式可以保证其一致性。
  
7.3.2 饿汉和懒汉模式

  饿汉模式:
  在类加载时就创建单例对象。
  优点:线程安全, 因为对象在类加载时就已经创建好了,不存在多线程并发创建的问题。简单直接,实现较为简单。
  缺点:无论是否使用,对象都会在类加载时创建,可能会造成肯定的资源浪费。
  1. template <typename T>
  2. class Singleton
  3. {
  4.         static T data;
  5.        
  6. public:
  7.         static T* GetInstance()
  8.         {
  9.                 return &data;
  10.         }
  11. };
复制代码
  
  懒汉模式:
  在第一次使用时才创建单例对象。
  优点:延迟对象的创建,只有在真正必要时才创建,节流了资源
  缺点:线程不安全,在多线程环境下可能会创建多个实例。必要额外的处理来保证线程安全,增加了实现的复杂性。
  1. template <typename T>
  2. class Singleton
  3. {
  4.         static T* inst;
  5. public:
  6.         static T* GetInstance()
  7.         {
  8.                 if (inst == NULL)
  9.                 {
  10.                         inst = new T();
  11.                 }
  12.                 return inst;
  13.         }
  14. };
复制代码
  
懒汉模式实现线程安全的线程池
线程安全的线程池:
  1. #pragma once
  2. #include <iostream>
  3. #include <vector>
  4. #include <queue>
  5. #include <string>
  6. #include <pthread.h>
  7. #include <unistd.h>
  8. #include "Task.hpp"
  9. struct ThreadData
  10. {
  11.     pthread_t tid;
  12.     std::string name;
  13. };
  14. static const int defaultnum=5; //默认线程数量
  15. //实现我们的线程池
  16. template<class T>
  17. class ThreadPool
  18. {
  19. public:
  20.     void Lock()
  21.     {
  22.         pthread_mutex_lock(&_mutex);
  23.     }
  24.     void Unlock()
  25.     {
  26.         pthread_mutex_unlock(&_mutex);
  27.     }
  28.     void Wakeup()
  29.     {
  30.         pthread_cond_signal(&_cond);
  31.     }
  32.     void ThreadSleep()
  33.     {
  34.         pthread_cond_wait(&_cond,&_mutex);
  35.     }
  36.     bool IsQueueEmpty()
  37.     {
  38.         return _tasks.empty();
  39.     }
  40.     std::string GetThreadName(pthread_t tid)
  41.     {
  42.         for (const auto &ti : _threads)
  43.         {
  44.             if (ti.tid == tid)
  45.                 return ti.name;
  46.         }
  47.         return "None";
  48.     }
  49. public:
  50.     //注意我们线程调用的函数要求参数和返回值都是void*
  51.     //但是handler在类中默认有this指针->参数不匹配,可以bind或者声明static或放在类外
  52.     static void *Handler(/*ThreadPool *this,*/void *args)
  53.     {
  54.         ThreadPool<T> *tp=static_cast<ThreadPool<T>*>(args);
  55.         std::string name = tp->GetThreadName(pthread_self());
  56.         while(true)
  57.         {
  58.             tp->Lock();
  59.             
  60.             while(tp->IsQueueEmpty()) //判断任务是否为空
  61.             {
  62.                 tp->ThreadSleep(); //条件变量
  63.             }
  64.             T t=tp->Pop(); //取出任务
  65.             tp->Unlock();
  66.             t(); //处理任务
  67.             std::cout<<name<<" run, "<<"result: "<< t.GetResult()<<std::endl;            
  68.         }
  69.         return nullptr;
  70.     }
  71.     void Start() //启动线程池
  72.     {
  73.         int num=_threads.size();
  74.         for(int i=0;i<num;i++)
  75.         {
  76.             _threads[i].name="thread-"+std::to_string(i+1);
  77.             pthread_create(&(_threads[i].tid),nullptr,Handler,this);
  78.         }
  79.     }
  80.     void Push(const T &t) //向任务队列放入任务
  81.     {
  82.         Lock();
  83.         _tasks.push(t); //放入任务
  84.         Wakeup(); //唤醒线程
  85.         Unlock();
  86.     }
  87.     T Pop() //取出任务
  88.     {
  89.         T t=_tasks.front();
  90.         _tasks.pop();
  91.         return t;
  92.     }
  93.     static ThreadPool<T> *GetInstance() //获取单例对象
  94.     {
  95.         if(nullptr==_tp) //创建单例对象后,不会再有申请和释放锁的操作
  96.         {
  97.             pthread_mutex_lock(&_lock); //保护临界资源
  98.             if(_tp==nullptr)
  99.             {
  100.                 std::cout<<"singleton create done"<<std::endl;
  101.                 _tp=new ThreadPool<T>();
  102.             }
  103.             pthread_mutex_unlock(&_lock);
  104.         }
  105.         return _tp;
  106.     }
  107. private:
  108.     ThreadPool(const ThreadPool<T>&)=delete;
  109.     const ThreadPool<T>& operator=(const ThreadPool<T>&)=delete;
  110.    
  111.     ThreadPool(int num=defaultnum)
  112.         :_threads(num)
  113.     {
  114.         pthread_mutex_init(&_mutex,nullptr);
  115.         pthread_cond_init(&_cond,nullptr);
  116.     }
  117.     ~ThreadPool()
  118.     {
  119.         pthread_mutex_destroy(&_mutex);
  120.         pthread_cond_destroy(&_cond);
  121.     }
  122. private:   
  123.     std::vector<ThreadData> _threads; //线程池
  124.     std::queue<T> _tasks; //任务队列
  125.     pthread_mutex_t _mutex; //锁
  126.     pthread_cond_t _cond; //条件变量
  127.     static ThreadPool<T> *_tp; //获取单例指针
  128.     static pthread_mutex_t _lock; //锁
  129. };
  130. template<class T>
  131. ThreadPool<T> *ThreadPool<T>::_tp=nullptr;
  132. template<class T>
  133. pthread_mutex_t ThreadPool<T>::_lock=PTHREAD_MUTEX_INITIALIZER;
复制代码
  
运行函数:
  1. #include <iostream>
  2. #include "ThreadPool.hpp"
  3. int main()
  4. {
  5.     //ThreadPool<Task> *tp=new ThreadPool<Task>(5);
  6.     //tp->Start();
  7.     sleep(2); //懒汉模式
  8.     ThreadPool<Task>::GetInstance()->Start();
  9.     srand(time(nullptr) ^ getpid());   
  10.     while(true)
  11.     {
  12.         //1. 构建任务
  13.         int x=rand()%10+1;
  14.         usleep(10);
  15.         int y=rand()%5;
  16.         char op=opers[rand()%opers.size()];
  17.         Task t(x,y,op);
  18.         //tp->Push(t);
  19.         ThreadPool<Task>::GetInstance()->Push(t);
  20.         //2. 交给线程池处理
  21.         std::cout<<"main thread make task: "<<t.GetTask()<<std::endl;
  22.         sleep(1);
  23.     }
  24.     return 0;
  25. }
复制代码
  

  
STL中的容器是否是线程安全的?
  不是。原因是 STL 的计划初衷是将性能发掘到极致,而一旦涉及到加锁保证线程安全,会对性能造成巨大的影响。
  而且对于差别的容器,加锁方式的差别,性能可能也差别(例如hash表的锁表和锁桶)。
因此 STL 默认不是线程安全。假如必要在多线程环境下使用,往往必要调用者自行保证线程安全。
  
智能指针是否是线程安全的?
  对于 unique_ptr,由于只是在当前代码块范围内生效,因此不涉及线程安全问题。
  对于 shared_ptr,多个对象必要共用一个引用计数变量,所以会存在线程安全问题。但是尺度库实现的时间思量到了这个问题,基于原子操作(CAS)的方式保证 shared_ptr 能够高效, 原子的操作引用计数。
  
8. 常见锁使用汇总

8.1 互斥锁(Mutex)

  确保在同一时候只有一个线程能够访问被保护的资源。
  例如,多个线程同时操作一个共享的全局变量时,使用互斥锁来保证数据的一致性。
  
  初始化互斥锁
  1. int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
复制代码
作用:初始化一个互斥锁。
参数:
  mutex:指向要初始化的互斥锁的指针。
  attr:互斥锁的属性指针,通常为 NULL(使用默认属性)。
返回值:乐成返回 0,失败返回错误码。
  
  加锁
  1. int pthread_mutex_lock(pthread_mutex_t *mutex);
复制代码
作用:获取互斥锁,假如锁已被占用则壅闭等待。
参数:mutex:要加锁的互斥锁指针。
返回值:乐成返回 0,失败返回错误码。
  
  尝试加锁
  1. int pthread_mutex_trylock(pthread_mutex_t *mutex);
复制代码
作用:尝试获取互斥锁,假如锁可用则获取并返回 0,否则立刻返回 EBUSY。
参数:mutex:要尝试加锁的互斥锁指针。
返回值:乐成返回 0,锁不可用返回 EBUSY。
  
  解锁
  1. int pthread_mutex_unlock(pthread_mutex_t *mutex);
复制代码
作用:释放已获取的互斥锁。
参数:mutex:要解锁的互斥锁指针。
返回值:乐成返回 0,失败返回错误码。
  
  销毁互斥锁
  1. int pthread_mutex_destroy(pthread_mutex_t *mutex);
复制代码
作用:销毁指定的互斥锁。
参数:mutex:要销毁的互斥锁指针。
返回值:乐成返回 0,失败返回错误码。
  
8.2 条件变量(Condition Variable)

  通常与互斥锁共同使用,用于线程之间的等待和关照。
  比如一个线程必要等待某个条件满足后才能继续实行,而另一个线程在条件满足时关照它。
  
  初始化条件变量
  1. int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
复制代码
作用:初始化一个条件变量。
参数:
  cond:指向要初始化的条件变量的指针。
  attr:条件变量的属性指针,通常为 NULL(使用默认属性)。
返回值:乐成返回 0,失败返回错误码。
  
  等待条件变量
  1. int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
复制代码
作用:壅闭当前线程,直到指定的条件变量被唤醒。
参数:
  cond:要等待的条件变量指针。
  mutex:与条件变量关联的互斥锁指针。
返回值:乐成返回 0,失败返回错误码。
  
  定时等待条件变量
  1. int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
复制代码
作用:壅闭当前线程,直到指定的条件变量被唤醒或到达指定的超时时间。
参数:
  cond:要等待的条件变量指针。
  mutex:与条件变量关联的互斥锁指针。
  abstime:指定的超时时间。
返回值:乐成返回 0,超时返回 ETIMEDOUT,失败返回其他错误码。
  
  唤醒一个等待条件变量的线程
  1. int pthread_cond_signal(pthread_cond_t *cond);
复制代码
作用:唤醒至少一个等待指定条件变量的线程。
参数:cond:要唤醒的条件变量指针。
返回值:乐成返回 0,失败返回错误码。
  
  唤醒所有等待条件变量的线程
  1. int pthread_cond_broadcast(pthread_cond_t *cond);
复制代码
作用:唤醒所有等待指定条件变量的线程。
参数:cond:要唤醒的条件变量指针。
返回值:乐成返回 0,失败返回错误码。
  
  销毁条件变量
  1. int pthread_cond_destroy(pthread_cond_t *cond);
复制代码
作用:销毁指定的条件变量。
参数:cond:要销毁的条件变量指针。
返回值:乐成返回 0,失败返回错误码。
  
8.3 信号量(Semaphore)

  用于控制同时访问某一资源的线程数量。
  例如限定同时访问数据库连接的线程数量。
  
  初始化信号量
  1. int sem_init(sem_t *sem, int pshared, unsigned int value);
复制代码
作用:初始化一个信号量。
参数:
  sem:指向要初始化的信号量的指针。
  pshared:表示信号量的共享属性,0 表示线程间共享,非 0 表示进程间共享。
  value:信号量的初始值。
返回值:乐成返回 0,失败返回 -1。
  
  等待信号量
  1. int sem_wait(sem_t *sem);
复制代码
作用:等待信号量的值大于 0,然后将其减 1。
参数:sem:要操作的信号量指针。
返回值:乐成返回 0,失败返回 -1。
  
  尝试等待信号量
  1. int sem_trywait(sem_t *sem);
复制代码
作用:尝试等待信号量,假如信号量的值大于 0,则将其减 1 并立刻返回;否则返回错误。
参数:sem:要操作的信号量指针。
返回值:乐成返回 0,信号量不可用返回 -1 并设置 errno 为 EAGAIN。
  
  释放信号量
  1. int sem_post(sem_t *sem);
复制代码
作用:将信号量的值增加 1。
参数:sem:要操作的信号量指针。
返回值:乐成返回 0,失败返回 -1。
  
  获取信号量的值
  1. int sem_getvalue(sem_t *sem, int *sval);
复制代码
作用:获取信号量的当前值,并将其存储在 sval 指向的变量中。
参数:
  sem:要操作的信号量指针。
  sval:用于存储信号量值的整数指针。
返回值:乐成返回 0,失败返回 -1。
  
  销毁信号量
  1. int sem_destroy(sem_t *sem);
复制代码
作用:销毁指定的信号量。
参数:sem:要销毁的信号量指针。
返回值:乐成返回 0,失败返回 -1。
  
8.4 自旋锁(Spin Lock)

  线程在获取锁失败时,会不停循环尝试获取,而不是壅闭等待。
  适用于锁被持有的时间很短的环境,能避免线程切换的开销,但假如锁被长时间持有,会浪费 CPU 资源。
  
  初始化自旋锁
  1. int spinlock_init(spinlock_t *lock, const spinlockattr_t *attr);
复制代码
作用:初始化指定的自旋锁。
参数:
  lock:指向要初始化的自旋锁的指针。
  attr:自旋锁属性指针,可为 NULL(使用默认属性)。
返回值:乐成返回 0,失败返回错误码。
  
  销毁自旋锁
  1. int spinlock_destroy(spinlock_t *lock);
复制代码
作用:销毁指定的自旋锁。
参数:lock:要销毁的自旋锁指针。
返回值:乐成返回 0,失败返回错误码。
  
  尝试获取自旋锁(读)
  1. int spinlock_rdlock(spinlock_t *lock);
复制代码
作用:尝试获取自旋锁的读锁。
参数:lock:指向要获取读锁的自旋锁的指针。
返回值:乐成返回 0,失败返回错误码。
  
  尝试获取自旋锁(写)
  1. int spinlock_wrlock(spinlock_t *lock);
复制代码
作用:尝试获取自旋锁的写锁。
参数:lock:指向要获取写锁的自旋锁的指针。
返回值:乐成返回 0,失败返回错误码。
  
  释放自旋锁
  1. int spinlock_unlock(spinlock_t *lock);
复制代码
作用:释放指定的自旋锁。
参数:lock:要释放的自旋锁指针。
返回值:乐成返回 0,失败返回错误码。
  
8.5 读写锁(Read-Write Lock)

  区分读操作和写操作。允很多个线程同时进行读操作,但在写操作时,不允许其他线程进行读或写操作。
  适用于读操作频繁而写操作较少的场景,比如共享数据的读取次数远多于修改次数的环境。
  
  初始化读写锁
  1. int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
  2.                                                 const pthread_rwlockattr_t *restrict attr);
复制代码
作用:初始化指定的读写锁。
参数:
  rwlock:指向要初始化的读写锁的指针。
  attr:读写锁属性指针,可为 NULL(使用默认属性)。
返回值:乐成返回 0,失败返回错误码。
  
  销毁读写锁
  1. int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
复制代码
作用:销毁指定的读写锁。
参数:rwlock:要销毁的读写锁指针。
返回值:乐成返回 0,失败返回错误码。
  
  获取读写锁的读锁
  1. int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
复制代码
作用:尝试获取指定读写锁的读锁。
参数:rwlock:指向要获取读锁的读写锁的指针。
返回值:乐成返回 0,失败返回错误码。
  
  获取读写锁的写锁
  1. int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
复制代码
作用:尝试获取指定读写锁的写锁。
参数:rwlock:指向要获取写锁的读写锁的指针。
返回值:乐成返回 0,失败返回错误码。
  
  释放读写锁
  1. int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
复制代码
作用:释放指定的读写锁。
参数:rwlock:要释放的读写锁指针。
返回值:乐成返回 0,失败返回错误码。
  
  设置读写锁的优先级
  1. int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
复制代码
作用:设置读写锁的优先级。
参数:
  attr:读写锁属性指针。
  pref:优先级选择,有以下 3 种:
  PTHREAD_RWLOCK_PREFER_READER_NP (默认设置)读者优先,可能会导致写者饥饿环境。
  PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和 PTHREAD_RWLOCK_PREFER_READER_NP 一致。
  PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁。
返回值:乐成返回 0,失败返回错误码。
  
其他概念:
  悲观锁:在每次取数据时,总是担心数据会被其他线程修改,所以会在取数据前先加锁(读锁,写锁,行锁等),当其他线程想要访问数据时,被壅闭挂起。
  乐观锁:每次取数据时间,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要接纳两种方式:版本号机制和CAS操作。
  公平锁:公平锁按照线程请求锁的先后次序来分配锁。先请求的线程先获取,保证了次序公平。保证次序,得当要求严格公平的场景。但性能开销大,高并发时吞吐量可能受影响。
  非公平锁:非公平锁不按请求次序分配锁,锁释放时竞争的线程都可能获取,不肯定是先请求的。性能好,高并发时吞吐量可能高。但可能导致线程饥饿,行为不太确定。
  CAS操作:当必要更新数据时,判断当前内存值和之前取得的值是否相当。假如相当则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不停重试。
            

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

没腿的鸟

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表