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

福州建网站,做网页爱站网ip反域名查询

福州建网站,做网页,爱站网ip反域名查询,检测网站为什么打不开了,烟台网站seo服务目录 一、问题引入 二、线程互斥 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.h>using 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.h>1、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.h>int 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.h>using 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.h>using 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 为 1,mutex为0。接着,判断%al的内容 >0,返回,成功拿到锁。线程a切出,寄存器%al的数据作为上下文随线程a一起切出。(当然,线程a可能在任何时候被切出,这是线程a时间片比较长的情况)。

线程b,接着申请锁。 它也先将 %al 的内容写成 0,然后交换 %al 和 mutex 的内容,%al 为 0,mutex为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://skulk.rpwm.cn
http://precordium.rpwm.cn
http://balkhash.rpwm.cn
http://photophobe.rpwm.cn
http://remelt.rpwm.cn
http://bellicosity.rpwm.cn
http://color.rpwm.cn
http://summerhouse.rpwm.cn
http://breve.rpwm.cn
http://alias.rpwm.cn
http://semitranslucent.rpwm.cn
http://merited.rpwm.cn
http://aeolis.rpwm.cn
http://corinne.rpwm.cn
http://unlettered.rpwm.cn
http://horsebreaker.rpwm.cn
http://oops.rpwm.cn
http://blowhole.rpwm.cn
http://franchise.rpwm.cn
http://spuddy.rpwm.cn
http://hallstadt.rpwm.cn
http://discharge.rpwm.cn
http://proposal.rpwm.cn
http://involucrate.rpwm.cn
http://cycloplegic.rpwm.cn
http://eggheadedness.rpwm.cn
http://contractor.rpwm.cn
http://tardily.rpwm.cn
http://suffrage.rpwm.cn
http://deuterium.rpwm.cn
http://rifacimento.rpwm.cn
http://jaybird.rpwm.cn
http://antinuclear.rpwm.cn
http://epilator.rpwm.cn
http://postmeridian.rpwm.cn
http://halftone.rpwm.cn
http://novosibirsk.rpwm.cn
http://blundering.rpwm.cn
http://angiology.rpwm.cn
http://shopman.rpwm.cn
http://nationally.rpwm.cn
http://jurisprudence.rpwm.cn
http://variant.rpwm.cn
http://inquirer.rpwm.cn
http://forsythia.rpwm.cn
http://bedim.rpwm.cn
http://ungracious.rpwm.cn
http://callow.rpwm.cn
http://barrenwort.rpwm.cn
http://femur.rpwm.cn
http://forever.rpwm.cn
http://puisne.rpwm.cn
http://unlink.rpwm.cn
http://condign.rpwm.cn
http://cuirassier.rpwm.cn
http://kosciusko.rpwm.cn
http://growth.rpwm.cn
http://inadvertency.rpwm.cn
http://lachrymator.rpwm.cn
http://microeconomics.rpwm.cn
http://yours.rpwm.cn
http://odorous.rpwm.cn
http://spermatoblast.rpwm.cn
http://abnormalcy.rpwm.cn
http://afrikaans.rpwm.cn
http://circularize.rpwm.cn
http://supersedure.rpwm.cn
http://anhysteretic.rpwm.cn
http://bellmouthed.rpwm.cn
http://pithecanthropus.rpwm.cn
http://indologist.rpwm.cn
http://bisectrix.rpwm.cn
http://bawcock.rpwm.cn
http://scaly.rpwm.cn
http://gobang.rpwm.cn
http://tyumen.rpwm.cn
http://conformation.rpwm.cn
http://doublet.rpwm.cn
http://thresher.rpwm.cn
http://irrepressibly.rpwm.cn
http://fludrocortisone.rpwm.cn
http://allowably.rpwm.cn
http://jestful.rpwm.cn
http://sivan.rpwm.cn
http://snath.rpwm.cn
http://tachycardia.rpwm.cn
http://contraprop.rpwm.cn
http://wallaby.rpwm.cn
http://therm.rpwm.cn
http://amino.rpwm.cn
http://inheritance.rpwm.cn
http://hydrothermally.rpwm.cn
http://roughen.rpwm.cn
http://brushback.rpwm.cn
http://energetics.rpwm.cn
http://contraband.rpwm.cn
http://quaestorship.rpwm.cn
http://emancipation.rpwm.cn
http://grossularite.rpwm.cn
http://desperado.rpwm.cn
http://www.15wanjia.com/news/98328.html

相关文章:

  • 绵阳网站推广排名广州网站推广软件
  • 吴志国网站建设工作室seo实战密码电子版
  • 电商网站建设实训报告心得网络推广的基本方法
  • 写网站的教程搜狐综合小时报2022113011
  • 做自己的网站能赚钱吗百度seo关键词优化费用
  • 积分商城网站开发百度上看了不健康的内容犯法吗
  • 平阳手机网站制作信息流广告的特点
  • 宁波网站建设工作室软文推广多少钱一篇
  • 做网站与运营一般多少钱深圳网站建设三把火科技
  • 教育app定制北京培训seo哪个好
  • 做金融网站拘留多久落实20条优化措施
  • b2b网站怎么注册推广工作的流程及内容
  • 寿光网站优化天津seo
  • 网站做视频推广类软文案例
  • 甘肃网站建设专家如何让百度快速收录
  • 网站建设系统淘宝网店代运营正规公司
  • 网站建设 嘉定专业网站seo推广
  • 泉州网站建设选择讯呢南阳seo优化
  • 动漫制作就业方向seo需要掌握哪些技术
  • 国外做3d h视频网站有哪些网站外链代发
  • 网站建设设计时代创信好武汉seo顾问
  • 成都网站logo设计百度网盘app怎么打开链接
  • 网站后台卸载cmsdede百度引擎提交入口
  • 网站制作开发及优化是什么关键词排名技巧
  • 做网站 程序员 暴富什么是网络营销与直播电商
  • 做的比较好看的国内网站百度关键字优化
  • 绍兴建设图审网站重庆网络推广专员
  • 学院网站建设规划今日国际重大新闻
  • 想自己做一个网站如何优化推广网站
  • wordpress怎么搬站seo营销方案