广州网站设计易企建站成品网站源码在线观看
广州网站设计易企建站,成品网站源码在线观看,wordpress后台登录不上,建设银行信用卡网站多少文章目录 线程线程互斥锁死锁 线程同步生产者消费者模型POSIX信号量基于环形队列的生产消费模型 线程池 线程
线程是进程内部可以独立运行的最小单位
进程是资源分配的基本单位#xff0c;线程是调度器调度的基本单位
线程在进程的地址空间内运行
进程内的大部分资源线程是… 文章目录 线程线程互斥锁死锁 线程同步生产者消费者模型POSIX信号量基于环形队列的生产消费模型 线程池 线程
线程是进程内部可以独立运行的最小单位
进程是资源分配的基本单位线程是调度器调度的基本单位
线程在进程的地址空间内运行
进程内的大部分资源线程是共享的但也有属于线程自己的独立资源主要是寄存器和栈位于共享区
为什么引入线程
创建新线程的工作要比创建新进程的工作少得多
线程的切换要比进程的切换所作的工作少得多
线程异常
单个线程出现崩溃会导致整个进程奔溃因为线程是在进程内部运行的
Linux下的线程
Linux将线程视为一种特殊类型的进程称作轻量级进程Lightweight ProcessLWP
Linux并没有提供系统调用来对线程进行操作对线程操作使用的是第三方库
来段代码感受一下
#include iostream
#include unistd.h
#include pthread.h
using namespace std;void *routine(void *args)
{while (true){printf(sub process running ... pid:%d\n, getpid());sleep(1);}
}int main()
{pthread_t t;pthread_create(t, nullptr, routine, nullptr);while (true){printf(main process running ... pid:%d\n, getpid());sleep(3);}pthread_join(t, nullptr);return 0;
}运行结果如下 程序运行起来之后检测可以看到有两个线程其中一个pid和lwp相等这就是主线程下面的就是新线程 每个进程内部至少有一个线程主线程
线程互斥
锁
多个线程在对同一份资源进行访问时很可能会造成数据紊乱的问题来看个例子
#include iostream
#include unistd.h
#include pthread.h
using namespace std;int num 10000;void *routine(void *args)
{while (true){if (num 0){usleep(100);printf(%d\n, num);num--;}else{break;}usleep(1000);}}int main()
{const int n 100;pthread_t t[n];for (size_t i 0; i n; i){pthread_create(t i, nullptr, routine, nullptr);}for (size_t i 0; i n; i){pthread_join(t[i], nullptr);}return 0;
}创建两个新线程让这两个线程对同一个数字进行争夺类似于抢票没有票就退出
只看最后的几个结果 这里n0时明显不能进入if语句但是后面居然打印出负数 这就是多线程对于同一份资源在进行访问时造成的数据紊乱
临界区
临界区是指一段代码当一个线程或进程进入这段代码并开始执行时其他线程或进程不能同时进入执行该段代码的区域。这是为了确保共享资源在同一时间只能被一个线程访问避免数据竞争和不一致的状态。
临界资源
临界资源是指在多线程或多进程环境中被共享访问的数据、对象或资源。因为多个线程或进程可能同时访问这些资源所以需要在访问它们时确保数据的一致性和正确性。
如何对临界资源进行保护------‘锁’
锁的使用可以确保当一个线程在访问共享资源时其他线程无法同时访问该资源从而保证数据的一致性和正确性。
对刚才代码稍作修改
#include iostream
#include unistd.h
#include pthread.h
using namespace std;pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;
int num 10000;void *routine(void *args)
{while (true){pthread_mutex_lock(mutex);if (num 0){usleep(100);printf(%d\n, num);num--;pthread_mutex_unlock(mutex);}else{pthread_mutex_unlock(mutex);break;}usleep(1000);}
}int main()
{const int n 100;pthread_t t[n];for (size_t i 0; i n; i){pthread_create(t i, nullptr, routine, nullptr);}for (size_t i 0; i n; i){pthread_join(t[i], nullptr);}pthread_mutex_destroy(mutex);return 0;
}这次运行结果就正常了
有了锁就能保证每次只有一个线程能够访问到临界资源
死锁
死锁是指两个或多个线程互相等待对方持有的资源而无法继续执行的情况。
死锁的4个必要条件
互斥一个资源每次只能被一个执行流使用请求与保持一个执行流因请求资源而阻塞时对已获得的资源保持不放不剥夺:一个执行流已获得的资源在末使用完之前不能强行剥夺循环等待:若干执行流之间形成一种头尾相接的循环等待资源的关系
避免死锁
破坏死锁的四个必要条件加锁顺序一致…
线程同步
在多个线程互斥的访问临界资源时只有一个能获得临界资源其他线程只能忙等待什么也做不了
两个问题
存在多个线程竞争同一个临界资源的情况每次都是其中一个线程获得临界资源造成其他线程的饥饿问题在临界资源没有就绪的时候线程不停的申请锁—判断临界资源事否就绪—释放锁一直重复不合理
如何解决条件变量 条件变量允许一个或多个线程等待满足某些条件时继续执行。
void push(const T val)
{pthread_mutex_lock(_mtx);while (is_full()){pthread_cond_wait(_full, _mtx);}_q.push(val);cout Producer Thread [ pthread_self() ] Produced val endl;pthread_mutex_unlock(_mtx);pthread_cond_signal(_empty);
}截取一段代码举例
要点
等待时线程自动挂起并且释放手中的锁如果收到唤醒信号再在被阻塞的位置唤醒重新去申请锁为什么使用while循环不用if。要避免伪唤醒if出现伪唤醒的情况时会直接向下运行而while循环会再次检查资源事否就绪防止伪唤醒
生产者消费者模型 核心点
生产者在阻塞队列已满时不能继续放入数据消费者在阻塞队列为空时不能继续取出数据生产者与生产者之间互斥消费者与消费者之间互斥
阻塞队列
为了代码的健壮性使用泛型编程
字段
阻塞队列是个队列所以封装STL的队列
阻塞队列属于临界区对临界区访问应该似乎互斥的所以需要一把锁来控制生产者和消费者的互斥访问
条件变量不让临界区外的资源忙等待
队列容量
函数
构造、析构
基本的入队列和出队列
判断是否空是否满
#pragma once#include iostream
#include queue
#include pthread.h
#include unistd.husing namespace std;
#define BLOCK_SIZE 10template class T
class BlockQueue
{
public:BlockQueue(size_t capacity BLOCK_SIZE): _capacity(capacity){pthread_mutex_init(_mtx, nullptr);pthread_cond_init(_full, nullptr);pthread_cond_init(_empty, nullptr);}void push(const T val){pthread_mutex_lock(_mtx);while (is_full()){pthread_cond_wait(_full, _mtx);}_q.push(val);cout Producer Thread [ pthread_self() ] Produced val endl;pthread_mutex_unlock(_mtx);pthread_cond_signal(_empty);}void pop(){pthread_mutex_lock(_mtx);while (isEmpty()){pthread_cond_wait(_empty, _mtx);}T s _q.front();_q.pop();cout Consumer Thread [ pthread_self() ] Consumed s endl;pthread_mutex_unlock(_mtx);pthread_cond_signal(_full);}~BlockQueue(){pthread_cond_destroy(_full);pthread_cond_destroy(_empty);pthread_mutex_destroy(_mtx);}private:bool isEmpty(){return _q.empty();}bool is_full(){return _capacity _q.size();}private:queueT _q;size_t _capacity;pthread_mutex_t _mtx;pthread_cond_t _full;pthread_cond_t _empty;
};POSIX信号量
信号量的两个操作
p操作申请资源v操作释放资源
基于环形队列的生产消费模型
允许生产者和消费者在同一个数据结构上进行操作 核心点
生产者不能套圈消费者消费者不能超过生产者生产者与生产者之间互斥消费者与消费者之间互斥
环形队列
字段
封装vector
容量
生产者在队列的位置
消费者在队列的位置
消费信号
生产信号
消费锁
生产锁
函数
构造这里没有实现析构因为封装的锁和信号量各自的析构已经实现析构时会自动调用
入队列和出队列
#include iostream
#include vector
#include Sem.hpp
#include Mutex.hpp
#define SIZE 10template class T
class RingQueue
{
public:RingQueue(size_t size SIZE): _size(size),_rq(size),_p_sem(size),_c_sem(0),_c_pos(0),_p_pos(0){}void push(const T val){_p_sem.p();_p_mtx.lock();_rq[_p_pos] val;_p_pos % _size;_p_mtx.unlock();_c_sem.v();}void pop(T *pv){_c_sem.p();_c_mtx.lock();*pv _rq[_c_pos];_c_pos % _size;_c_mtx.unlock();_p_sem.v();}private:std::vectorT _rq;size_t _size;size_t _c_pos;size_t _p_pos;Sem _c_sem;Sem _p_sem;Mutex _c_mtx;Mutex _p_mtx;
};要点
为什么是先申请资源p操作再加锁申请信号量实际上一种“预定”先买票后入座这样可以确保每个进入临界区的线程要访问的资源已经就绪可以提升效率。
对锁进行封装
#pragma once
#include pthread.h
class Mutex
{public:Mutex(){pthread_mutex_init(_mtx, nullptr);}~Mutex(){pthread_mutex_destroy(_mtx);}void lock(){pthread_mutex_lock(_mtx);}void unlock(){pthread_mutex_unlock(_mtx);}private:pthread_mutex_t _mtx;
};对信号量进行封装
#pragma once#include semaphore.h
class Sem
{
public:Sem(int value){sem_init(_sem, 0, value);}~Sem(){sem_destroy(_sem);}void p(){sem_wait(_sem);}void v(){sem_post(_sem);}private:sem_t _sem;
};线程池
详情代码见实现简易线程池
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/86559.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!