线程与进程
对于 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);