百度网站关键词优化在哪里做,做静态网站的参考文献,重庆安全监督工程信息网,备案号查询平台官网一、线程安全
1.1 可重入 VS 线程安全
1.1.1 概念
线程安全#xff1a;多个线程并发执行同一段代码时#xff0c;不会出现不同的结果。常见对全局变量或者静态变量进行操作#xff0c;并且没有锁的保护的情况下#xff0c;会出现问题。重入#xff1a;同一个函数被不同…一、线程安全
1.1 可重入 VS 线程安全
1.1.1 概念
线程安全多个线程并发执行同一段代码时不会出现不同的结果。常见对全局变量或者静态变量进行操作并且没有锁的保护的情况下会出现问题。重入同一个函数被不同的执行流调用当前一个流程还没有执行完就有其他的执行力再次进入一个函数在重入的情况下运行结果不会出现任何不同或者任何问题则该函称之为可重入函数否则为不可重入函数。线程安全是线程在执行中的相互关系重入是函数的特点引起线程安全有很多种情况重入是其中的一种
1.1.2 常见的线程不安全的情况
不保护共享变量的函数函数状态随着被调用状态发生变化的函数返回指向静态变量指针的函数调用线程不安全函数的函数
1.1.3 对函数状态随着被调用状态发生变化进行解释
class A
{
public:void fun(){std::cout fun std::endl;}
}class B : public class A
{int count 0;
public:void test(){fun();count;std::cout count std::endl;}
}
1.2 常见的线程安全的情况
每个线程对全局变量或者静态变量只有读取的权限而没有写入的权限一般来说这些线程是安全的类或者接口对于线程来说都是原子操作多个线程之间的切换不会导致该接口的执行结果存在二义性
1.3 常见不可重入的情况
调用了malloc/free函数因为malloc函数是用全局链表带管理堆的调用了标准的I/O库函数标准的I/O库函数的很多实现都以不可重入的方式使用全局数据结构可重入函数体内使用了静态的数据结构
1.4 常见的可重入的情况
不使用全局变量或静态变量不使用malloc/free开辟的空间不调用不可重入函数不返回静态或去全局数据所有数据都有函数的调用者提供使用本地数据或者通过制作全局数据的本地拷贝来保护全局数据
1.5 可重入与线程安全的联系
函数是可重入的那就是线程安全的线程安全不一定是可重入那就不能有多个线程使用有可能引发线程安全问题如果一个函数中有全局变量那么这个函数既不是线程安全的也不是可重入的
1.6 可重入与线程安全的区别
可重入函数是线程安全函数的一种线程安全不一定是可重入的而可重入函数则一定是线程安全的如果将对临界资源的访问加上锁则这个函数是线程安全的但如果这个重入函数若锁还未释放则会产生死锁因此是不可重入的。
二、常见锁的概念
2.1 死锁的概念 死锁是指在一组进程中的各个进程均占有不会释放的资源但因互相申请被其他进程所占用不会释放的资源而处于一种永久等待状态。
2.2 出现死锁的场景
在加锁之后又进行了一次加锁操作现在有两个线程线程A和线程B。两个线程都要互相申请两个锁才能进行继续访问但是由于访问的顺序不同会造成死锁的现象 2.3 死锁的四个必要条件
互斥条件一个资源每次只能被一个执行流使用请求与保持条件一个执行流因请求资源而阻塞时对已获得的资源保持不放不剥夺条件一个执行流已经获得的资源在未使用完之前不能强行剥夺循环等待条件若干个执行流之间形成一种头尾相接的循环等待资源的关系
2.4 避免死锁
破坏死锁的四个必要条件加锁顺序一致避免锁未释放的场景资源一次性分配
三、Linux线程同步
3.1 条件变量
当一个线程互斥地访问某个变量时它可能发现在其他线程改变状态之前他什么也做不了例如一个线程访问队列时发现队列为空它只能等待直到其他线程将一个节点添加到队列中。这种情况就需要使用到条件变量
3.2 同步概念与竞态条件
同步在保证数据安全的前提下让线程能够按照某种特定的顺序访问临界资源从而有效避免饥饿问题叫做同步竞态条件因为时序问题而导致程序异常我们称之为竞态条件在线程场景下这种问题也不难理解
四、STL、智能指针和线程安全
4.1 STL中的容器是否是线程安全的 STL中的容器不是线程安全的因为STL的设计初衷是将性能挖掘到极致而一旦涉及到加锁保证线程安全会对性能造成巨大的影响而且对于不同的容器加锁方式的不同性能也可能不同例如hash表的锁表和锁桶 因此STL默认不是线程安全的如果需要在多线程环境下使用往往需要调用者自行保证线程安全。
4.2 智能指针是否是线程安全的 对于unique_ptr由于只是在当前代码块范围内生效因此不涉及线程安全问题 对于shared_ptr多个对象需要共用一个引用计数变量所以会存在线程安全问题但是标准库实现的时候考虑到这个问题基于原子操作的方式保证shared_ptr能够足够高效原子的操作引用计数。
五、线程安全的单例模式有待学习
5.1 什么是单例模式 单例模式属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例。
5.2 单例模式的特点 某些类只应该具有一个对象实例就称之为单例。例如一个男人只能有一个媳妇。 在很多服务器开发场景中经常需要让服务器加载很多的数据到内存中往往需要用一个单例的类来管理这些数据。
5.3 饿汉实现方式和懒汉实现方式
举个例子
吃完饭立刻洗碗这种就是饿汉方式。因为下一顿吃的时候可以立刻拿着碗就能吃饭吃完饭先把碗放下然后下一顿饭用到了这个碗再洗这个碗这就是懒汉方式。
懒汉方式最核心的思想是延时加载从而能够优化服务器的启动速度。
5.3.1 饿汉方式实现单例模式
templatetypename T
class Singleton{static T date;
public:static T* GetTnstance() {return date;}
}
// 只要通过Singleton这个包装类来使用T对象则一个进程中只有一个T对象的实例
5.3.2 懒汉方式实现单例模式
templatetypename T
class Singleton
{static T* inst;
public:static T* GetInstance(){if(inst nullptr){inst new T();}return inst;}
}; 存在一个严重的问题线程不安全。如果在第一次调用GetInstance的时候 两个线程同时调用可能会创建出两份T对象的实例但是后续再次调用就没有问题了。
5.4 将线程池改为懒汉方式实现单例模式线程安全版本
// 添加单例
static ThreadPoolT *_instance;
static pthread_mutex_t _lock;template class T
ThreadPoolT *ThreadPoolT::_instance;template class T
pthread_mutex_t ThreadPoolT::_lock PTHREAD_MUTEX_INITIALIZER;
static ThreadPoolT *GetInstance()
{// 如果是多线程调用以下的代码就会有问题// 所以我们需要进行加锁// 利用双判断的方式可以有效减少获取单例的加锁成本而且保证线程安全// 保证第二次之后所有的线程不用在加锁直接返回if (nullptr _instance){LockGuard lockguard(_lock);if (nullptr _instance){_instance new ThreadPoolT;_instance-InitThreadPool();_instance-Start();LOG(DEBUG, 创建线程池单例);return _instance;}}LOG(DEBUG, 获取线程池单例);return _instance;
}// 赋值拷贝警用
ThreadPoolT operator(const ThreadPoolT ) delete;
ThreadPool(const ThreadPoolT ) delete;
六、其他常见的各种锁
6.1 悲观锁 在每次读取数据时总是担心数据会被其他线程修改所以会在取数据前先加锁读锁写锁行锁等当其他线程想要访问数据时被阻塞挂起
6.2 乐观锁 每次取数据的时候总是乐观的认为数据不会被其他线程修改因此不上锁但是在更新数据前会判断其他数据在更新前有没有对数据进行修改主要采用两种方式版本号机制和CAS操作。
6.2.1 版本号机制 一般是在数据表中加上一个数据版本号version字段表示数据被修改的次数当数据被修改时version值会加一。当线程A要更新数据值时在读取数据的同时也会读取version值在提交更新时若刚才读取到的version值为当前数据库中的version值相等时才更新否则重试更新操作直到更新成功。
6.2.2 CAS操作 当需要更新数据时判断当前内存值和之前取得的值是否相等如果相等则用新值更新如果不相等则失败。失败之后需要进行重试一般是一个自旋过程即不断重试。
6.3 自旋锁 在之前的学习中我们从来没有讨论过在临界区里线程执行的时长问题如果时间比较久推荐其他线程阻塞挂起等待如果时间比较短推荐其他线程不要休眠阻塞挂起而是不断一直抢占锁直到申请成功自旋。 自旋的过程中用户会发现自旋锁和之前学习的互斥锁在行为上是相似的都是阻塞在那里。 七、读者写者问题
7.1 引入读者写者问题
读者写者问题的例子写文章打印报纸、杂志出黑板报
读者总多写者较少——读者写者问题最常见的情况有线程向公共资源中写入其他线程从公共资源中读取数据——读者写者问题
7.1.1 321 原则
3种关系读者与读者没有关系写者与写者互斥读者与写者互斥和同步2种角色读者写者1种场景公共资源
7.1.2 生产者消费者模型与读者写者问题的本质区别
读者和消费者的本质区别消费者会把数据拿走而读者不会把数据拿走只会进行拷贝
7.2 模拟实现一下读者写者的加锁逻辑 对于公共资源来说创建一个全局变量读者锁和写者锁。但是在实际中只要一个读者锁。
int reader_count 0;
pthread_mutex_t wlock;
pthread_mutex_t rlock;
对于读者来说
lock(rlock); // 先将读者加锁
if(reader_count 0)
{lock(wlock); // 变量为空说明第一次读将写者加锁// 这种操作只会进行一次,否则就有死锁//如果申请成功继续运行不会有任何读者进来//如果申请失败阻塞
}reader_count;
unlock(rlock);// 开始进行常规的readlcok(rlock);
--read_count;
if(read_count 0) // 如果读者数量为0则可以唤醒写者
{unlock(wlock);
}
unlock(rlock);
对于写者来说
lock(wlock);// 写入操作unlock(wlock);
7.3 了解一下系统中读写锁的接口 7.3.1 初始化读写锁 函数的原型 #include pthread.hint pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t* restrict attr); 函数的功能 进行初始化读写锁函数的参数 rwlock指向创建的读写锁对象attr属性一般置为nullptr 函数的返回值 成功返回 0 失败直接返回错误号 7.3.2 销毁读写锁 函数的原型 #include pthread.hint pthread_rwlock_destroy(pthread_rwlock_t *rwlock); 函数的功能 将所创建好的读写锁进行销毁函数的参数 rwlock执行所要销毁的读写锁的指针 函数的返回值 成功返回 0 失败直接返回错误号 7.3.3 给读者锁加锁
函数的原型
#include pthread.hint pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
7.3.4 给写者锁加锁
函数的原型
#include pthread.hint pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
7.3.5 给读者锁和写者锁解锁
函数的原型
#include pthread.hint pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
7.3.6 代码部分
#include pthread.h
#include stdio.h
#include iostream// 读写锁的概念int count 0; // 共享资源
pthread_rwlock_t rwlock; // 创建一个读写锁#define NUM 5// 读者
void *reader(void *arg)
{pthread_rwlock_rdlock(rwlock); // 给读者加锁std::cout Reader conut: count std::endl;pthread_rwlock_unlock(rwlock); // 进行解锁return nullptr;
}// 写者
void *writer(void *arg)
{pthread_rwlock_wrlock(rwlock); // 给写者加锁count;pthread_rwlock_unlock(rwlock); // 给读者解锁return nullptr;
}int main()
{pthread_t reader_threads[NUM], writer_threads;pthread_rwlock_init(rwlock, nullptr); // 给读写锁进行初始化pthread_create(writer_threads, nullptr, writer, nullptr);for (int i 0; i NUM; i){pthread_create(reader_threads[i], nullptr, reader, nullptr);}pthread_join(writer_threads, nullptr);for (int i 0; i NUM; i){pthread_join(reader_threads[i], nullptr);}pthread_rwlock_destroy(rwlock);return 0;
}