当前位置: 首页 > news >正文

西宁市网站建设网站建设入门教学

西宁市网站建设,网站建设入门教学,上海网页建站模板,浏览器在线打开网页引入互斥初识锁互斥量mutex锁原理解析 可重入VS线程安全STL中的容器是否是线程安全的? 死锁 引入 我们写一个多线程同时访问一个全局变量的情况(抢票系统)#xff0c;看看会出什么bug#xff1a; // 共享资源#xff0c; 火车票 int tickets 10000; //新线程执行方法 vo… 引入互斥初识锁互斥量mutex锁原理解析 可重入VS线程安全STL中的容器是否是线程安全的? 死锁 引入 我们写一个多线程同时访问一个全局变量的情况(抢票系统)看看会出什么bug // 共享资源 火车票 int tickets 10000; //新线程执行方法 void *getTicket(void *args) {std::string username static_castconst char *(args);while (true){if (tickets 0){usleep(1254); // 1秒 1000毫秒 1000 000 微妙 10^9纳秒std::cout username 正在进行抢票: tickets std::endl;// 用这段时间来模拟真实的抢票要花费的时间tickets--;}else{break;}}return nullptr; } //主线程 //跟之前一样创建多个线程然后调用这个getTicket方法就行假如创建4个线程同时抢票总票数有10000张每个线程抢到票以后减一按照正常情况我们应该是抢票到0截至。 多个线程交叉执行本质就是让调度器尽可能的频繁发生线程调度与切换 线程一般在什么时候发生切换呢时间片到了来了更高优先级的线程线程等待的时候。 线程是在什么时候检测上面的问题呢从内核态返回用户态的时候线程要对调度状态进行检测如果可以就直接发生线程切换 实验结果假如此时tickets 1第1号线程先判断了if (tickets 0)然后进入语句中结果被usleep阻塞了这时候切换了另外的线程此时tickets的值还未被1号进程更改所以它同样也能进入语句中就这样导致tickets--被执行了多次然后就出现上述的负数结果。 对变量进行或者–在C、C上看起来只有一条语句但是汇编之后至少是三条语句: 从内存读取数据到CPU寄存器中在寄存器中让CPU进行对应的算逻运算写回新的结果到内存中变量的位置 模拟一下线程1与线程2的更改切换逻辑 我们定义的全局变量在没有保护的时候往往是不安全的像上面多个线程在交替执行造成的数据安全问题发生了数据不一致问题! 互斥 初识锁 临界资源多个执行流进行安全访问的共享资源临界区我们把多个执行流中访问临界资源的代码往往是线程代码的很小的一部分互斥想让多个线程串行访问共享资源任何时刻互斥保证有且只有一个执行流进入临界区访问临界资源通常对临界资源起保护作用原子性对一个资源进行访问的时候要么不做要么做完该操作只有两态不会被任何调度机制打断的操作。只用一条汇编语句就能执行完的算是原子当然还有别的情况之后再详细说明 初步使用加锁解锁 //定义为全局的锁无需初始化和销毁直接以这样的形式使用: pthread_mutex_t lock PTHREAD_MUTEX_INITIALIZER; void *getTicket(void *args) {std::string username static_castconst char *(args);while (true){pthread_mutex_lock(lock);if (tickets 0){usleep(1254); // 1秒 1000毫秒 1000 000 微妙 10^9纳秒std::cout username 正在进行抢票: tickets std::endl;tickets--;pthread_mutex_unlock(lock);}else{pthread_mutex_unlock(lock);break;}}return nullptr; }运行现象 我们可以很明显感受到运行的速度变慢了而且这里的线程4一直在抢票别的线程没有机会 加锁和解锁的过程多个线程串行执行的程序变慢了锁只规定互斥访问没有规定必须让谁优先执行锁就是真是的让多个执行流进行竞争的结果 线程解锁后立马又申请锁导致别的线程竞争不过我们可以在每次循环末尾增加一段阻塞时间 如何看待锁 锁本身就是一个共享资源我们一份代码要使用锁前提是我们能看到这个锁这个资源全局的变量是要被保护的锁是用来保护全局的资源的锁本身也是全局资源锁的安全谁来保护呢pthread_mutex_lock、pthread_mutex_unlock:加锁的过程必须是安全的加锁的过程其实是原子的如果申请成功就继续向后执行如果申请暂时没有成功执行流会阻塞谁持有锁谁进入临界区 对锁的思考 如果线程1申请锁成功进入临界资源正在访问临界资源期间其他线程在做什么阻塞等待如果线程1申请锁成功进入临界资源正在访问临界资源期间线程1可不可以被切换呢绝对可以的。当持有锁的线程被切走的时候是抱着锁被切走的即便自己被切走了其他线程依旧无法申请锁成功也便无法向后执行直到我最终释放这个锁! 所以对于其他线程而言有意义的锁的状态无非两种1.申请锁前2.释放锁后。站在其他线程的角度看待当前线程持有锁的过程就是原子的 未来我们在使用锁的时候一定要尽量保证临界区的粒度(锁中间保护代码的多少)要非常小 有人可能会想加锁也未必安全比如我让线程12加锁去访问公共资源线程3不加锁去访问公共资源这样的话公共资源依旧没有被保护起来。加锁是程序员行为必须做到要加就都要加 互斥量mutex 大部分情况线程使用的数据都是局部变量变量的地址空间在线程栈空间内这种情况变量归属单个线程其他线程无法获得这种变量。这种说法其实不准确如果硬要访问还是有可能做到但有时候很多变量都需要在线程间共享这样的变量称为共享变量可以通过数据的共享完成线程之间的交互。多个线程并发的操作共享变量会带来一些问题。 --操作并不是原子操作而是对应三条汇编指令 load 将共享变量ticket从内存加载到寄存器中update : 更新寄存器里面的值执行-1操作store 将新值从寄存器写回共享变量ticket的内存地址 要解决以上问题需要做到三点 代码必须要有互斥行为当代码进入临界区执行时不允许其他线程进入该临界区。如果多个线程同时要求执行临界区的代码并且临界区没有线程在执行那么只能允许一个线程进入该临界区。如果线程不在临界区中执行那么该线程不能阻止其他线程进入临界区。 要做到这三点本质上就是需要一把锁。Linux上提供的这把锁叫互斥量 初始化互斥量有两种方法 静态分配: pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER动态分配: int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrictattr); 参数 mutex要初始化的互斥量 attrNULL销毁互斥量需要注意 使用 PTHREAD_MUTEX_INITIALIZER 初始化的互斥量不需要销毁不要销毁一个已经加锁的互斥量已经销毁的互斥量要确保后面不会有线程再尝试加锁 int pthread_mutex_destroy(pthread_mutex_t *mutex)互斥量加锁和解锁 int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); 返回值:成功返回0,失败返回错误码调用 pthread_ lock 时可能会遇到以下情况 互斥量处于未锁状态该函数会将互斥量锁定同时返回成功发起函数调用时其他线程已经锁定互斥量或者存在其他线程同时申请互斥量但没有竞争到互斥量那么pthread_ lock调用会陷入阻塞(执行流被挂起)等待互斥量解锁。 锁原理解析 经过上面的例子大家已经意识到单纯的 i 或者 i 都不是原子的有可能会有数据一致性问题为了实现互斥锁操作大多数体系结构都提供了swap或exchange指令该指令的作用是把寄存器和内存单元的数据相交换由于只有一条指令保证了原子性即使是多处理器平台访问内存的总线周期也有先后一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。 现在我们把lock和unlock的伪代码改一下 加锁执行图 接下来的代码就是判断寄存器中的代码与数据是否符合。 在线程1执行后面的内容时时刻都可能被切换但是切换了线程2寄存器的内容也会被切换掉这样就算怎么执行线程2寄存器中始终都是0。再切换回线程1由上下文保护寄存器内容切换回原来的数字1。 解锁movb $1, mutex就是将1重新给mutex 这个mutex原本的1就像是一个令牌它有且只有一个谁先抢到就能先运行 可重入VS线程安全 线程安全多个线程并发同一段代码时不会出现不同的结果。常见对全局变量或者静态变量进行操作并且没有锁保护的情况下会出现该问题。 重入同一个函数被不同的执行流调用当前一个流程还没有执行完就有其他的执行流再次进入我们称之为重入。一个函数在重入的情况下运行结果不会出现任何不同或者任何问题则该函数被称为可重入函数否则是不可重入函数。 常见的线程不安全的情况 不保护共享变量的函数函数状态随着被调用状态发生变化的函数返回指向静态变量指针的函数调用线程不安全函数的函数 常见的线程安全的情况 每个线程对全局变量或者静态变量只有读取的权限而没有写入的权限一般来说这些线程是安全的类或者接口对于线程来说都是原子操作多个线程之间的切换不会导致该接口的执行结果存在二义性 常见不可重入的情况 调用了malloc/free函数因为malloc函数是用全局链表来管理堆的调用了标准I/O库函数标准I/O库的很多实现都以不可重入的方式使用全局数据结构可重入函数体内使用了静态的数据结构 常见可重入的情况 不使用全局变量或静态变量不使用用malloc或者new开辟出的空间不调用不可重入函数不返回静态或全局数据所有数据都有函数的调用者提供使用本地数据或者通过制作全局数据的本地拷贝来保护全局数据 可重入与线程安全联系 函数是可重入的那就是线程安全的函数是不可重入的那就不能由多个线程使用有可能引发线程安全问题如果一个函数中有全局变量那么这个函数既不是线程安全也不是可重入的。 可重入与线程安全区别 可重入函数是线程安全函数的一种线程安全不一定是可重入的而可重入函数则一定是线程安全的。如果将对临界资源的访问加上锁则这个函数是线程安全的但如果这个重入函数若锁还未释放则会产生死锁因此是不可重入的。 STL中的容器是否是线程安全的? 不是安全的原因是 STL 的设计初衷是将性能挖掘到极致而一旦涉及到加锁保证线程安全会对性能造成巨大的影响。而且对于不同的容器加锁方式的不同性能可能也不同(例如hash表的锁表和锁桶)。因此 STL 默认不是线程安全如果需要在多线程环境下使用往往需要调用者自行保证线程安全。 智能指针是否是线程安全的? 对于 unique_ptr由于只是在当前代码块范围内生效因此不涉及线程安全问题。 对于 shared_ptr多个对象需要共用一个引用计数变量所以会存在线程安全问题。但是标准库实现的时候考虑到了这个问题基于原子操作(CAS)的方式保证 shared_ptr 能够高效原子的操作引用计数。 死锁 死锁是指在一组进程中的各个进程均占有不会释放的资源但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。 感性认识死锁小明与小红各自有5毛钱它们一起去商店买棒棒糖商店老板说它们的棒棒糖1块钱一个小明对小红说能不能把她的5毛钱给他这样他将能凑1块钱买棒棒糖小红不乐意她反问小明能不能把他的5毛钱给自己这样她就能买棒棒糖它们互相僵持最后都没买到棒棒糖。他们之前出现的互相僵持的情况就是死锁 死锁四个必要条件 互斥条件一个资源每次只能被一个执行流使用请求与保持条件一个执行流因请求资源而阻塞时对已获得的资源保持不放不剥夺条件一个执行流已获得的资源在末使用完之前不能强行剥夺循环等待条件若干执行流之间形成一种头尾相接的循环等待资源的关系 避免死锁方法 破坏死锁的四个必要条件加锁顺序一致避免锁未释放的场景资源一次性分配 避免死锁算法死锁检测算法、银行家算法 如有错误或者不清楚的地方欢迎私信或者评论指出
http://www.tj-hxxt.cn/news/226313.html

相关文章:

  • 腾讯云手动搭建wordpress个人站点做网站的公司多吗
  • 国内jsp网站有哪些低价网站建设湘潭
  • 企业网站定制开发流程网站如何做分享
  • 模板做的网站 怎么提升排名品牌设计需要学什么
  • 自己怎么创建免费网站吗进一步加大网站集约化建设力度
  • 先建网站还是先做网页wordpress推介链接插件
  • 哪些网站做推广性价比高黄冈网站推广优化技巧
  • 做衣服哪个网站好保定市做网站的电话
  • 网站做icp备案需要多久单位网站建设费用什么会计科目
  • 婚庆网站制作西安做网站陕西必达
  • wordpress 网站地图插件wordpress图片尺寸 样式
  • 建筑网站推荐艺术网站建设
  • 建设部相关网站利用html做博客网站
  • 网站负责人 备案网站空间的权限
  • 商务网站建设sz886网站引流推广
  • 成都六度网站建设网站制作全包多少钱
  • 模板网站哪个平台好有经验的扬中网站建设
  • 绿色风格的网站南京建设网站多少钱
  • 网站怎么做可以合法让别人充钱微商城开店
  • 网站模板更换厦门网站建设设
  • 企业网站备案要求厦门建设局咨询电话
  • 电子简历模板抖音seo排名优化公司
  • 深圳麒麟网站建设青岛网站建设代理加盟
  • 网站推广的目的是什么网页素材及网站架构制作
  • 怀化租房网站关键词优化公司费用多少
  • 企业网站完整版vi设计百科
  • 江苏省教育网站官网找别人建网站去哪里
  • 建一个网站要多久如何充实网站内容
  • 网站开发页面适应高度商家商城小程序
  • 织梦网站栏目建设商丘微网站