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

做网站的功能结构布局平台优化方案

做网站的功能结构布局,平台优化方案,杭州关键词排名提升,最好网站制作工具目录 一、问题引入 二、线程互斥 1、相关概念 2、加锁保护 1、静态分配 2、动态分配 3、锁的原理 4、死锁 三、可重入与线程安全 1、概念 2、常见的线程不安全的情况 3、常见的线程安全的情况 4、常见不可重入的情况 5、常见可重入的情况 6、可重入与线程安全联系…目录 一、问题引入 二、线程互斥 1、相关概念 2、加锁保护 1、静态分配 2、动态分配 3、锁的原理 4、死锁 三、可重入与线程安全 1、概念 2、常见的线程不安全的情况 3、常见的线程安全的情况 4、常见不可重入的情况 5、常见可重入的情况 6、可重入与线程安全联系 7、可重入与线程安全区别 一、问题引入 大部分情况线程使用的数据都是局部变量变量的地址空间在线程栈空间内这种情况变量归属单个线程其他线程无法获得这种变量。 但有时候很多变量都需要在线程间共享这样的变量称为共享变量可以通过数据的共享完成线程之间的交互。多个线程并发的操作共享变量会带来一些问题。 我们来看看下面的多线程抢票系统的代码 #include iostream #include unistd.h #include cerrno #include cstring #include pthread.husing namespace std;int ticket 100;void *getticket(void *arg) {char *name (char *)arg;while (true){if (ticket 0){usleep(1000);cout name : ticket endl;ticket--;}elsebreak;} }int main() {pthread_t tid1, tid2, tid3, tid4;pthread_create(tid1, nullptr, getticket, (void *)thread 1);pthread_create(tid2, nullptr, getticket, (void *)thread 2);pthread_create(tid3, nullptr, getticket, (void *)thread 3);pthread_create(tid4, nullptr, getticket, (void *)thread 4);pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);pthread_join(tid3, nullptr);pthread_join(tid4, nullptr);return 0; } 这里的ticket变量是一个全局变量那么它就会被所有线程共享。创建线程后所有线程访问getticket函数对其进行了重入访问ticket并对ticket--。但是我们发现票数出现了负数这完全不符合我们的代码逻辑和想要的结果。这是为什么呢 首先程序在编译的时候会被编译成汇编代码 而在汇编代码中ticket--操作在我们看来只有一行代码但是在汇编中它其实分为了三步1、将ticket值拷入到CPU寄存器中2、CPU对其进行--操作3、将结果写回内存。 而我们知道进程是有时间片的在执行完上面任意一步时线程可能因为时间片到了而被切换。而这就会造成一些问题。如下图 线程A先进入在完成第二步 -- 操作后因为时间片到了要被切换出去99作为上下文数据被保存起来随A一起被切换。线程B进入因为B的时间片比较长他把ticket值减到了50并写回了内存后时间片到了被切换。线程A再次进入CPU把上下文恢复然后接着第3步执行直接把99写到了内存里面。 线程B明明已经让ticket的值减到了50结果你个线程A又直接把结果改成了99。这样就出现了数据错乱的现象。 在我们对ticket进行并发访问的时候由于ticket- - 操作并不是原子的所以出现了数据不一致的情况。这种情况怎么解决呢我们接着往下讲。 二、线程互斥 1、相关概念 1、临界资源多线程执行流共享的资源就叫做临界资源。 2、临界区每个线程内部访问临界资源的代码就叫做临界区。 3、互斥任何时刻互斥保证有且只有一个执行流进入临界区访问临界资源通常对临界资源起保护作用。 4、原子性不会被任何调度机制打断的操作该操作只有两态要么完成要么未完成。 2、加锁保护 为了解决上面代码的数据不一致的问题需要做到三点 1、代码必须要有互斥行为当代码进入临界区执行时不允许其他线程进入该临界区。 2、如果多个线程同时要求执行临界区的代码并且临界区没有线程在执行那么只能允许一个线程进入该临界区。 3、如果线程不在临界区中执行那么该线程不能阻止其他线程进入临界区。 而其中最简单的一种方法就是对临界资源进行加锁保护。以达到下面的效果 定义和初始化锁的函数  NAMEpthread_mutex_destroy, pthread_mutex_init - destroy and initialize a mutexSYNOPSIS#include pthread.h1、int pthread_mutex_destroy(pthread_mutex_t *mutex);2、int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);3、pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t 是由原生线程库给用户提供的一个数据类型就是我们常说的锁。上图的 1和2 是对锁进行局部定义时的销毁和初始化操作相当于析构函数和构造函数。 上图的 3 是对全局锁或者static静态锁进行初始化的方式。下面我们一一讲解。 加锁和解锁函数 发起函数调用时其他线程已经锁定互斥量或者存在其他线程同时申请锁但没有竞争到互斥量那么pthread_ lock调用会陷入阻塞(执行流被挂起)等待互斥量解锁再去申请锁。 NAMEpthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock - lock andunlock a mutexSYNOPSIS#include pthread.hint pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_trylock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex); 1、静态分配 静态分配就是我们 3 对应的对锁定义和初始化的方式。我们使用它对抢票代码进行保护。 #include iostream #include unistd.h #include cstring #include time.h #include pthread.husing namespace std;pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; int ticket 100;void *getticket(void *arg) {char *name (char *)arg;while (true){pthread_mutex_lock(mutex); // 加锁保护其他线程只能在这阻塞等待直到拿到锁if (ticket 0) // 这部分代码只能串行执行{usleep(rand() % 10000);cout name : ticket endl;ticket--;pthread_mutex_unlock(mutex); // 访问完临界资源解锁// 让其他线程能够拿锁访问}else{pthread_mutex_unlock(mutex); // 访问完临界资源解锁// 让其他线程能够拿锁访问break;}usleep(rand() % 2000000);}return nullptr; }int main() {srand((unsigned long)time(nullptr) ^ getpid() ^ 433);pthread_t tid1, tid2, tid3, tid4;pthread_create(tid1, nullptr, getticket, (void *)thread 1);pthread_create(tid2, nullptr, getticket, (void *)thread 2);pthread_create(tid3, nullptr, getticket, (void *)thread 3);pthread_create(tid4, nullptr, getticket, (void *)thread 4);pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);pthread_join(tid3, nullptr);pthread_join(tid4, nullptr);return 0; } 注加锁的时候一定要保证加锁粒度越小越好。最好不要让一些非临界区也被加锁保护。 2、动态分配 如果我们定义的锁是一个局部变量那么我们就要像下面的代码这样使用锁 #include iostream #include unistd.h #include cstring #include time.h #include pthread.husing namespace std; #define THREAD_NUM 5class threaddata { public:threaddata(const string s, pthread_mutex_t *m): name(s), mtx(m){}public:string name;pthread_mutex_t *mtx; };int ticket 100;void *getticket(void *arg) {threaddata *td (threaddata *)arg;while (true){pthread_mutex_lock(td-mtx);if (ticket 0) {usleep(rand() % 10000);cout td-name : ticket endl;ticket--;pthread_mutex_unlock(td-mtx);}else{pthread_mutex_unlock(td-mtx);break;}usleep(rand() % 2000000);}delete td;return nullptr; }int main() {pthread_mutex_t mtx;pthread_mutex_init(mtx, nullptr);srand((unsigned long)time(nullptr) ^ getpid() ^ 433);pthread_t t[THREAD_NUM];for (int i 0; i THREAD_NUM; i){string name thread ;name to_string(i 1);threaddata *td new threaddata(name, mtx);pthread_create(t i, nullptr, getticket, (void *)td);}for (int i 0; i THREAD_NUM; i)pthread_join(t[i], nullptr);pthread_mutex_destroy(mtx);return 0; } 3、锁的原理 通过加锁我们能够保证执行临界资源的操作是原子的。可是访问临界资源时多个线程要申请同一把锁那么就必须要能够看到同一把锁那么这个锁不就成了一个临界资源了吗那锁是怎么保证自己的安全的呢 为了保证锁的安全申请和释放锁的操作也必须是原子的。如何保证呢 在汇编的角度如果只有一行汇编语句我们就认为该汇编语句的执行是原子的。一般来说是使用swap或exchange指令以一条汇编语句将内存和CPU寄存器的数据进行交换。如下图 线程a是第一个申请锁的。它先将 %al 的内容写成 0然后交换 %al 和 mutex 的内容%al 为 1mutex为0。接着判断%al的内容 0返回成功拿到锁。线程a切出寄存器%al的数据作为上下文随线程a一起切出。当然线程a可能在任何时候被切出这是线程a时间片比较长的情况。 线程b接着申请锁。 它也先将 %al 的内容写成 0然后交换 %al 和 mutex 的内容%al 为 0mutex为0。接着判断%al的内容不大于0于是线程b挂起等待。只有线程a将锁释放后才能重新申请锁。 4、死锁 死锁多线程场景中 多个执行流彼此申请对方的锁资源并且还不释放自己已申请的锁资源进而导致执行流无法继续向下执行代码的现象。 产生死锁四个必要条件 1、互斥条件一个资源每次只能被一个执行流使用。 2、请求与保持条件一个执行流因请求资源而阻塞时对已获得的资源保持不放。 3、不剥夺条件一个执行流已获得的资源在末使用完之前不能强行剥夺。 4、循环等待条件若干执行流之间形成一种头尾相接的循环等待资源的关系。 避免产生死锁 1、破坏死锁的四个必要条件 2、加锁顺序一致 3、避免锁未释放的场景 4、资源一次性分配 三、可重入与线程安全 1、概念 ~ 线程安全多个线程并发同一段代码时不会出现不同的结果。常见对全局变量或者静态变量进行操作并且没有锁保护的情况下会出现该问题。 ~ 重入同一个函数被不同的执行流调用当前一个流程还没有执行完就有其他的执行流再次进入我们称之为重入。一个函数在重入的情况下运行结果不会出现任何不同或者任何问题则该函数被称为可重入函数否则是不可重入函数。 2、常见的线程不安全的情况 1、不保护共享变量的函数。 2、函数状态随着被调用状态发生变化的函数。 3、返回指向静态变量指针的函数。 4、调用线程不安全函数的函数。 3、常见不可重入的情况 1、调用了malloc/free函数因为malloc函数是用全局链表来管理堆的。 2、调用了标准I/O库函数标准I/O库的很多实现都以不可重入的方式使用全局数据结构。 3、可重入函数体内使用了静态的数据结构。 4、可重入与线程安全联系 1、函数是可重入的那就是线程安全的 2、函数是不可重入的那就不能由多个线程使用有可能引发线程安全问题 3、如果一个函数中有全局变量那么这个函数既不是线程安全也不是可重入的。 5、可重入与线程安全区别 1、可重入函数是线程安全函数的一种 2、线程安全不一定是可重入的而可重入函数则一定是线程安全的。 3、如果将对临界资源的访问加上锁则这个函数是线程安全的但如果这个重入函数若锁还未释放则会产生死锁因此是不可重入的。
http://www.zqtcl.cn/news/814526/

相关文章:

  • 网站怎么赚钱的网站asp源码
  • 明星网站怎么设计新手怎么做网络销售
  • ps做网站72分辨率深鑫辉网站建设
  • 购物网站设计的目的html简单登录页面代码
  • 网站导航栏下载网页自助建站
  • 新手建立网站的步骤建设企业网站个人网银
  • 俄罗斯女孩制作论文网站wordpress和hexo
  • 南宁市网站设计wordpress主题安装教程
  • 网站取消备案怎样做国外电子商务网站
  • 学校建设网站费用申请青岛平台公司
  • 平面设计师个人网站怎样登录韵网网站
  • 怎么用eclipse做网站开发推广平台取名字
  • 深圳建网站服务商广东佛山建网站
  • 网站推广公司卓立海创英文网站建设需求
  • 无锡网站营销公司简介最专业网站建设公司首选
  • 中文网站建设小组ios开发者账号申请
  • 月熊志网站福州建网站 做网页
  • 不同的网站有不同的风格宁波设计网站公司
  • 学校网站制作平台电子政务门户网站建设代码
  • 产品推广的网站怎么做网站标题与关键词
  • 青蛙网站建设wordpress修改logo
  • 网站套餐方案引擎搜索对人类记忆的影响
  • 滨州市滨城区建设局网站扎金花网站怎么做
  • 网站开发中视屏怎样编辑到网页上常州建站公司模板
  • 视频涉台互联网网站怎么做1cpu0.5g服务器用来做网站
  • 营销型网站设计官网怎么做网站优化 sit
  • 怎样获得做网站的客户免费企业网站程序上传
  • 新闻排版设计用什么软件网站seo诊断分析
  • 手机网站端域名怎样做解析一诺摄影设计
  • 网站开发行业竞争大吗郑州百度推广代运营公司