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

廊坊营销型网站建设网站开发目标

廊坊营销型网站建设,网站开发目标,分析网站结构,wordpress如何自建站文章目录 线程互斥互斥量 mutex初始化互斥量加锁与解锁 可重入和线程安全常见的线程安全情况常见的线程安全的情况常见的不可重入情况常见的可重入情况可重入与线程安全联系可重入与线程安全区别 死锁死锁的四个必要条件避免死锁 线程互斥 进程线程间的互斥相关背景概念 临界… 文章目录 线程互斥互斥量 mutex初始化互斥量加锁与解锁 可重入和线程安全常见的线程安全情况常见的线程安全的情况常见的不可重入情况常见的可重入情况可重入与线程安全联系可重入与线程安全区别 死锁死锁的四个必要条件避免死锁 线程互斥 进程线程间的互斥相关背景概念 临界资源 多线程执行流共享的资源叫做临界资源。临界区 每个线程内部访问临界资源的代码就叫做临界区。互斥 任何时刻互斥保证有且只有一个执行流进入临界区访问临界资源通常对临界资源起保护作用。原子性 不会被任何调度机制打断的操作该操作只有两态要么完成要么未完成。 注临界资源可能会因为多个线程同时访问这块资源导致数据错乱。 #include iostream #include cstring #include pthread.h #include unistd.h #include sys/types.h #include sys/syscall.h using namespace std;// int 票数计数器 int tickets 1000; // 临界资源可能会因为共同访问造成数据不一致的问题void *getTickets(void *args) {const char *name static_castconst char *(args);while (true){// 临界区if (tickets 0){usleep(1000);cout name 抢到了票, 票的编号: tickets endl;tickets--;}else{break;}}return nullptr; } int main() {pthread_t tid1;pthread_t tid2;pthread_t tid3;pthread_t tid4;pthread_create(tid1, nullptr, getTickets, (void *)thread 1);//线程1pthread_create(tid2, nullptr, getTickets, (void *)thread 2);//线程2pthread_create(tid3, nullptr, getTickets, (void *)thread 3);//线程3pthread_create(tid4, nullptr, getTickets, (void *)thread 4);//线程4pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_join(tid3,NULL);pthread_join(tid4,NULL);return 0; }可以看到明明就设置了1000张票怎么会出现编号为-1、-2的票呢 这是因为tickets--的操作不是原子性的而是分为三个步骤 将共享变量tickets从内存加载到寄存器中更新寄存器里面的值 执行-1操作将新值从寄存器写回共享变量tickets的内存地址 汇编代码如下 既然–操作要经历三个步骤才能完成 那么有可能thread1刚刚把1000读进cpu就被切换了 当该线程被切换的时候会保存它对应的上下文信息 1000这个数据当然也在里面 之后thread1被挂起。 之后我们的thread2进程就被调度了 因为当thread1被切换的时候内存中的tickek值并没有被改变 所以说thread2看到的值还是1000 我们假设thread2的竞争性比较强 它执行了100次之后才被切换 那么此时的ticket的值就由1000变成了900。 当thread2切换挂起之后我们的thread1回来继续执行 此时恢复它的上下文数据 由于上次保存时它寄存器中的数据是1000 所以说再经历23两步操作之后变为999之后加载到内存中 于是内存中的数据便从900变成999了 相当于此时多了1000张票。 从上面的流程中我们可以看出 --ticket这个操作并不是原子性的 那么我们如何解决上面的问题呢 其实思路很简单 我们只需要将--ticket这个操作变成原子性的就好了 那么怎么将它变成原子性的呢 我们的策略是加锁。 互斥量 mutex 要解决如上的问题就得做到以下三点 代码必须有互斥行为当代码进入临界区执行时 不允许其他线程进入该临界区如果多个线程同时要求执行临界区的代码 并且此时临界区没有线程在执行 那么只能允许一个线程进入该临界区如果线程不在临界区中执行 那么该线程不能阻止其他线程进入临界区 我们可以使用如下代码来定义一个互斥量。 pthread_mutex_t mutex;初始化互斥量 #include pthread.hint pthread_mutex_destroy(pthread_mutex_t *mutex); int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;返回值成功返回0失败返回错误号。 pthread_mutex_init函数对Mutex做初始化参数attr设定Mutex的属性如果attr为NULL则表示缺省属性。用pthread_mutex_init函数初始化的Mutex可以用pthread_mutex_destroy销毁。如果Mutex变量是静态分配的全局变量或static变量也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化相当于用pthread_mutex_init初始化并且attr参数为NULL。 加锁与解锁 #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);返回值成功返回0失败返回错误号。 一个线程可以调用pthread_mutex_lock获得Mutex如果这时另一个线程已经调用pthread_mutex_lock获得了该Mutex则当前线程需要挂起等待直到另一个线程调用pthread_mutex_unlock释放Mutex当前线程被唤醒才能获得该Mutex并继续执行。 如果一个线程既想获得锁又不想挂起等待可以调用pthread_mutex_trylock如果Mutex已经被另一个线程获得这个函数会失败返回EBUSY而不会使线程挂起等待。 #include iostream #include cstring #include pthread.h #include unistd.h #include sys/types.h #include sys/syscall.h using namespace std;// int 票数计数器 int tickets 1000; // 临界资源可能会因为共同访问造成数据不一致的问题 //pthread_mutex_t MutexPTHREAD_MUTEX_INITIALIZER; pthread_mutex_t Mutex; void *getTickets(void *args) {const char *name static_castconst char *(args);while (true){pthread_mutex_lock(Mutex);// 临界区if (tickets 0){usleep(100);cout name 抢到了票, 票的编号: tickets endl;tickets--;}else{break;}pthread_mutex_unlock(Mutex);}return nullptr; } int main() {pthread_mutex_init(Mutex,NULL);pthread_t tid1;pthread_t tid2;pthread_t tid3;pthread_t tid4;pthread_create(tid1, nullptr, getTickets, (void *)thread 1);//线程1pthread_create(tid2, nullptr, getTickets, (void *)thread 2);//线程2pthread_create(tid3, nullptr, getTickets, (void *)thread 3);//线程3pthread_create(tid4, nullptr, getTickets, (void *)thread 4);//线程4pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_join(tid3,NULL);pthread_join(tid4,NULL);return 0; }这样就不会出现票数小于等于0的情况了但是为什么一直都是thread 4抢到票呢其他的线程为什么不能抢票 这是因为锁的钥匙已经被线程4早早地拿走了其他线程没有钥匙是打不开锁的就只能在锁区域外等候。 锁是否需要被保护呢 锁是不许要被保护的因为从汇编层面保证了加锁是原子性的争夺锁只有两种可能要么拿到了要么没有拿到。 为了实现互斥锁的操作大多数体系结构都提供了swap或exchange指令该指令的作用就是把寄存器和内存单元的数据相交换。 以加锁示例这是由多态汇编语句执行的上述%al是寄存器mutex就是内存中的一个变量。每个线程申请锁时都要执行上述语句执行步骤如下 movb $0%al先将al寄存器中的值清0。该动作可以被多个线程同时执行因为每个线程都有自己的一组寄存器上下文信息执行该动作本质上是将自己的al寄存器清0。注意凡是在寄存器中的数据全部都是线程的内部上下文多个线程看起来同时在访问寄存器但是互不影响。 xchgb %almutex然后用此一条指令交换al寄存器和内存中mutex的值xchgb是体系结构提供的交换指令该指令可以完成寄存器和内存单元之间数据的交换。 最后判断al寄存器中的值是否大于0。若大于0则申请锁成功此时就可以进入临界区访问对应的临界资源否则申请锁失败需要被挂起等待直到锁被释放后再次竞争申请锁。 示例假设内存中有一个变量mutex为1cpu内部有%al寄存器我们有threadA和threadB俩线程 **示例**现在线程A要开始加锁执行上述语句。首先movb $0%al线程A把0读进al寄存器清0寄存器然后执行第二条语句xchgb %almutex将al寄存器中的值与内存中mutex的值进行交换。 交换完成后寄存器al的值为1内存中mutex的值为0。此时这个过程就是加锁 当线程A争议执行第三条语句if判断时发生了线程切换切至线程B但是线程A要把自己的上下文1带走。线程B也要执行加锁动作同样是第一条语句把0加载到寄存器清0寄存器。 随后线程B执行第二条语句交换动作可是mutex的数据先前已经被线程A交换至寄存器然后保存到线程A的上下文了现在的mutex为0而线程B执行交换动作拿寄存器al的0去换内存中mutex的0。 即使我线程A在执行第一条语句把寄存器清0后就发生了线程切换切至线程B线程A保存上下文数据0此时线程B执行第一条语句把0写进寄存器随后线程B执行第二条语句xchgb交换 此时线程A执行第三条语句if判断失败只能被挂起等待线程A只能把自己的上下文数据保存重新切换至线程B也就是说我线程B只要不运行你们其它所有线程都无法申请成功。线程B恢复上下文数据1到内存然后执行第三条语句if成功返回结果 **注意**上述xchgb就是申请锁的过程。申请锁是将数据从内存交换到寄存器本质就是将数据从共享内存变成线程私有。 mutex就是内存里的全局变量被所有线程共享但是一旦用一条汇编语句将内存的mutex值交换到寄存器寄存器内部是哪个线程使用那么此mutex就是哪个线程的上下文数据那么就意味着交换成功后其它任何一个线程都不可能再申请锁成功了因为mutex已经独属于某线程私有了。这个mutex 1就如同令牌一般哪个线程先交换拿到1那么哪个线程就能申请锁成功所以加锁是原子的当线程释放锁时需要执行以下步骤 将内存中的mutex置回1。使得下一个申请锁的线程在执行交换指令后能够得到1形象地说就是“将锁的钥匙放回去”。唤醒等待Mutex的线程。唤醒这些因为申请锁失败而被挂起的线程让它们继续竞争申请锁。 总结 在申请锁时本质上就是哪一个线程先执行了交换指令那么该线程就申请锁成功因为此时该线程的al寄存器中的值就是1了。而交换指令就只是一条汇编指令一个线程要么执行了交换指令要么没有执行交换指令所以申请锁的过程是原子的。在线程释放锁时没有将当前线程al寄存器中的值清0这不会造成影响因为每次线程在申请锁时都会先将自己al寄存器中的值清0再执行交换指令。CPU内的寄存器不是被所有的线程共享的每个线程都有自己的一组寄存器但内存中的数据是各个线程共享的。申请锁实际就是把内存中的mutex通过交换指令原子性的交换到自己的al寄存器中。 可重入和线程安全 线程安全 多个线程并发同一段代码时 不会出现不同的结果 常见对全局变量或者静态变量进行操作 并且没有锁保护的情况下 会出现线程安全问题重入 同一个函数被不同的执行流调用 当前一个流程还没有执行完 就有其他的执行流再次进入 我们称之为重入 一个函数在重入的情况下 运行结果不会出现任何不同或者任何问题 则该函数被称为可重入函数 否则是不可重入函数 常见的线程安全情况 不保护共享变量的函数函数状态随着被调用 状态发生变化的函数返回指向静态变量指针的函数调用线程不安全函数的函数 常见的线程安全的情况 每个线程对全局变量或者静态变量只有读取的权限 而没有写入的权限 一般来说这些线程是安全的类或者接口对于线程来说都是原子操作多个线程之间的切换不会导致该接口的执行结果存在二义性 常见的不可重入情况 调用了malloc/free函数 因为malloc函数是用全局链表来管理堆的调用了标准I/O库函数 标准I/O可以的很多实现都是以不可重入的方式使用全局数据结构可重入函数体内使用了静态的数据结构 常见的可重入情况 不使用全局变量或静态变量不使用malloc或者new开辟出的空间不调用不可重入函数不返回静态或全局数据 所有数据都由函数的调用者提供使用本地数据或者通过制作全局数据的本地拷贝来保护全局数据 可重入与线程安全联系 函数是可重入的 那就是线程安全的函数是不可重入的 那就不能由多个线程使用 有可能引发线程安全问题如果一个函数中有全局变量 那么这个函数既不是线程安全也不是可重入的 可重入与线程安全区别 可重入函数是线程安全函数的一种线程安全不一定是可重入的 而可重入函数则一定是线程安全的如果对临界资源的访问加上锁 则这个函数是线程安全的 但如果这个重入函数的锁还未释放则会产生死锁 因此是不可重入的 死锁 一般情况下如果同一个线程先后两次调用lock在第二次调用时由于锁已经被占用该线程会挂起等待别的线程释放锁然而锁正是被自己占用着的该线程又被挂起而没有机会释放锁因此就永远处于挂起等待状态了这叫做死锁Deadlock。另一种典型的死锁情形是这样线程A获得了锁1线程B获得了锁2这时线程A调用lock试图获得锁2结果是需要挂起等待线程B释放锁2而这时线程B也调用lock试图获得锁1结果是需要挂起等待线程A释放锁1于是线程A和B都永远处于挂起状态了。 写程序时应该尽量避免同时获得多个锁如果一定有必要这么做则有一个原则如果所有线程在需要多个锁时都按相同的先后顺序常见的是按Mutex变量的地址顺序获得锁则不会出现死锁。比如一个程序中用到锁1、锁2、锁3它们所对应的Mutex变量的地址是锁1锁2锁3那么所有线程在需要同时获得2个或3个锁时都应该按锁1、锁2、锁3的顺序获得。如果要为所有的锁确定一个先后顺序比较困难则应该尽量使用pthread_mutex_trylock调用代替pthread_mutex_lock调用以免死锁。 Case1因为自己拿了锁又去申请这把锁导致死锁了。 #include iostream #include cstring #include pthread.h #include unistd.h #include sys/types.h #include sys/syscall.h using namespace std;// int 票数计数器 int tickets 1000; // 临界资源可能会因为共同访问造成数据不一致的问题 //pthread_mutex_t MutexPTHREAD_MUTEX_INITIALIZER; pthread_mutex_t Mutex; void *getTickets(void *args) {const char *name static_castconst char *(args);while (true){pthread_mutex_lock(Mutex);pthread_mutex_lock(Mutex);// 临界区if (tickets 0){usleep(100);cout name 抢到了票, 票的编号: tickets endl;tickets--;}else{break;}pthread_mutex_unlock(Mutex);pthread_mutex_unlock(Mutex);}return nullptr; } int main() {pthread_mutex_init(Mutex,NULL);pthread_t tid1;pthread_create(tid1, nullptr, getTickets, (void *)thread 1);//线程1pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_join(tid3,NULL);pthread_join(tid4,NULL);return 0; }Case2互相申请对方的锁。 #include iostream #include cstring #include pthread.h #include unistd.h #include sys/types.h #include sys/syscall.h using namespace std;// int 票数计数器 int tickets 1000; // 临界资源可能会因为共同访问造成数据不一致的问题 pthread_mutex_t MutexPTHREAD_MUTEX_INITIALIZER; pthread_mutex_t Mutex1PTHREAD_MUTEX_INITIALIZER; void *getTickets(void *args) {const char *name static_castconst char *(args);while (true){pthread_mutex_lock(Mutex);sleep(1);pthread_mutex_lock(Mutex1);// 临界区if (tickets 0){usleep(100);cout name 抢到了票, 票的编号: tickets endl;tickets--;}else{break;}pthread_mutex_unlock(Mutex1);pthread_mutex_unlock(Mutex);}return nullptr; }void *getTickets1(void *args) {const char *name static_castconst char *(args);while (true){pthread_mutex_lock(Mutex1);sleep(1);pthread_mutex_lock(Mutex);// 临界区if (tickets 0){usleep(100);cout name 抢到了票, 票的编号: tickets endl;tickets--;}else{break;}pthread_mutex_unlock(Mutex);pthread_mutex_unlock(Mutex1);}return nullptr; }int main() {pthread_t tid1;pthread_t tid2;pthread_create(tid1, nullptr, getTickets, (void *)thread 1);//线程1pthread_create(tid2, nullptr, getTickets1, (void *)thread 2);//线程2pthread_join(tid1,NULL);pthread_join(tid2,NULL);return 0; }死锁的四个必要条件 互斥条件 一个资源每次只能被一个执行流使用请求与保持条件 一个执行流因请求资源而阻塞时 对已获得的资源保持不放不剥夺条件 一个执行流已获得的资源 在未使用完之前 不能强行剥夺循环等待条件 若干执行流之间形成一种头尾相接的循环等待资源的关系 注意 这是四个必要条件 也就是说四个条件全部满足才能够形成死锁 避免死锁 破坏死锁的四个必要条件。加锁顺序一致避免锁未释放的场景资源一次性分配
http://wiki.neutronadmin.com/news/267906/

相关文章:

  • 网页设计和网站编辑网站收录工具
  • php的网站有哪些模板网站不可以做seo优化吗
  • 网站开发中设置会员等级wordpress 安桌应用
  • 彩票游戏网站开发百度下载应用
  • 网站的横幅怎么做的北京网站制作网站
  • 网站建设制作包括哪些做网站需要好多图片
  • c 网站开发案例大全小程序开发难度大吗
  • 找建设网站公司哪家好wordpress 支付宝收钱
  • 手机网站的做wordpress 制作模板教程
  • 阳泉建设局网站濮阳网络教育
  • 网站投注建设淘宝官网首页电脑版登录入口
  • 网络舆情监测专业seo在线教学
  • 帮别人做海报网站asp做一个简单网站
  • 企业网站推广新桥做网站公司
  • 咨询网站开发做搜索网站
  • 相关网站怎么做公司企业邮箱有哪些
  • 福田区网站建wordpress leaf
  • php做网站的优势网站备案依据
  • 做像淘宝网的网站郑州企业网站快速优化多少钱
  • wpsppt网站链接怎么做万网官网登陆
  • 定制级高端网站建设网站开发和
  • 网站被k文章修改进口博览会2022
  • 网站建设的税率阳光保险官方网站
  • 建设部网站1667号公告长沙 外贸网站建设公司
  • 网站建设设计服务商企业管理咨询合同书范本
  • 网站排名推广怎么做网页设计提升班
  • 网站的搜索功能一般怎么做如何做网站改版
  • 51做网站网站个性化设计
  • 已注册域名怎么做网站呢网站建设师薪资
  • 房产中介网站建设技巧网站设计时应考虑哪些因素