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

建设好网站能赚到钱吗?企业网页制作方案

建设好网站能赚到钱吗?,企业网页制作方案,企业网站要更新文章吗,wordpress 更新很慢一、原子类型 1.多线程下的问题 在C中#xff0c;一个全局数据在多个线程中被同时使用时#xff0c;如果不加任何处理#xff0c;则会出现数据同步的问题。 #include iostream #include thread #include chrono long val 0;void test() {for (i…一、原子类型 1.多线程下的问题 在C中一个全局数据在多个线程中被同时使用时如果不加任何处理则会出现数据同步的问题。 #include iostream #include thread #include chrono long val 0;void test() {for (int i 0; i 10000000; i) {val;} }int main() {auto time1 std::chrono::duration_caststd::chrono::milliseconds(std::chrono::system_clock::now().time_since_epoch()).count();std::thread thread1(test);std::thread thread2(test);thread1.join();thread2.join();auto time2 std::chrono::duration_caststd::chrono::milliseconds(std::chrono::system_clock::now().time_since_epoch()).count();time2 - time1;std::cout val: val ((time2 - time1)/1000.0)sstd::endl; } 上述例子中test函数对全局变量val进行累加并在thread1和thread2两个线程中分别处理test函数。得到的结果如下 val:11260278 0.105s val的值并不是期望的20000000这是因为val操作不是原子操作导致的。 对于val实际上会被拆为三步 从内存读取val值到寄存器寄存器自增将寄存器值回写到val的内存 而多线程同时操作时实际步骤可能为 thread1读取val值为0thread2读取val值为0thread1增加val值为1thread2增加val值为1(注意与thread1增加的不是一个原始值)thread1写入val值为1thread2覆盖写入val值为1 这样就会导致两个线程的操作重复最终结果小于20000000。 2.原子操作 所谓原子操作指的是多线程中最小且不可并行化的操作如val语句的三步在一个线程中执行完之前不会运行其他的进程。 在C11之前原子操作都是通过“互斥”(即在临界区间内一个线程正在访问则其他线程会等待)处理的如互斥锁。下面则是通过添加互斥锁处理这个问题 #include mutexlong val 0;std::mutex locker; void test() {for (int i 0; i 10000000; i) {locker.lock();val;locker.unlock();} }输出结果如下 val:20000000 5.648s 可以看到val值正确但是性能消耗非常大。 3.原子类型 C11将原子操作抽象引入原子类型atomic并提供相应的操作接口原子操作。通过atomic实现线程间数据同步 #include atomicstd::atomic_long val 0; void test() {for (int i 0; i 10000000; i) {val;} } 输出为 val:20000000 2.29s 可见val值正确且耗时比互斥锁小了很多。(因为mutex使用涉及多次atomic原子指令操作用户态和内核态切换、线程切换调度开销、全局锁hash queue维护等开销所以时间更长。但是atomic只能对变量而锁可以针对范围内的所有内容) 3.1 内置原子类型 类似于前面的atomic_longC11为所有内置类型都提供了对应的原子类型 3.2 自定义类型原子类型 因为atomic为类模板所以可以通过 std::atomicT t; 创建自定义类型的原子类型当然也可以使用此方式创建内置类型的原子类型。 atomic为作为类模板提供了统一的操作接口 其中is_lock_free用于判断是否有锁load用于读取store用于存exchange用于交换数据。 由于原子类型属于资源类型所以为了避免拷贝时引起的问题atomic类模板删除了相关的拷贝构造和赋值函数。 此外atomic到原始类型的转换也是允许的(隐式的)但非原子操作。 atomic_flag atomic_flag是无锁的仅支持test_and_set和clear两个接口。其中test_and_set表示 如果atomic_flag原始为false则设置其为true并返回false如果原始为true则不处理并返回true 而clear则表示将atomic_flag置为false。 所以可以使用atomic_flag实现一个自旋锁 #include thread #include atomic #include iostream #include unistd.h using namespace std; std::atomic_flag lock ATOMIC_FLAG_INIT; void f(int n) {while (lock.test_and_set(std::memory_order_acquire)) // 尝试获得锁cout Waiting from thread n endl; // 自旋cout Thread n starts working endl; } void g(int n) {cout Thread n is going to start. endl;lock.clear();cout Thread n starts working endl; } int main() {lock.test_and_set(); //设为truethread t1(f, 1);thread t2(g, 2);t1.join();usleep(100);t2.join(); } // 编译选项:g -stdc11 6-3-3.cpp -lpthread上述代码声明了一个全局的atomic_flag变量lock。最开始将lock初始化为值ATOMIC_FLAG_INIT即false的状态。 而在线程t1中执行函数f的代码我们不停地通过lock的成员test_and_set来设置lock为true。这里的test_and_set()是一种原子操作用于在一个内存空间原子地写入新值并且返回旧值。因此test_and_set会返回之前的lock的值。 所以当线程t1执行join之后由于在main函数中调用过test_and_set因此f中的test_and_set将一直返回true并不断打印信息即自旋等待。 而当线程t2加入运行的时候由于其调用了lock的成员clear将lock的值设为false因此此时线程t1的自旋将终止从而开始运行后面的代码。这样一来我们实际上就通过自旋锁达到了让t1线程等待t2线程的效果。 当然还可以将lock封装为锁操作比如 void Lock(atomic_flag *lock) { while (lock.test_and_set ()); } void Unlock(atomic_flag *lock) { lock.clear(); } 二、内存模型、顺序一致性和memory_order 原子类型为线程间数据同步提供了一定的保障但是这是建立在顺序一致性的内存模型基础上。 1.问题 #include thread #include atomic #include iostream using namespace std; atomicint a {0}; atomicint b {0}; int ValueSet(int) {int t 1;a t;b 2; } int Observer(int) {cout ( a , b ) endl; // 可能有多种输出 } int main() {thread t1(ValueSet, 0);thread t2(Observer, 0);t1.join();t2.join();cout Got ( a , b ) endl; // Got (1, 2) } // 编译选项:g -stdc11 6-3-4.cpp -lpthread 对于上面的代码比较合理的打印值有(0,0),(1,0),(1,2)然而在非顺序一致性时可能会打印(02)这样的结果。 这是因为对于非顺序一致性场景下编译器在认定a、b的赋值语句的执行先后顺序对输出结果有任何的影响的话则可以依情况将指令重排序reorder以提高性能。因此就有可能会将b的赋值语句提前到a的赋值语句之前从而得到(0,2)这样的结果。 当然默认情况下在C11中的原子类型的变量在线程中总是保持着顺序执行的特性非原子类型则没有必要因为不需要在线程间进行同步。我们称这样的特性为“顺序一致”的即代码在线程中运行的顺序与程序员看到的代码顺序一致a的赋值语句永远发生于b的赋值语句之前。 2.内存模型 通常情况下内存模型是一个硬件的概念表示机器指令以什么样的顺序被执行。 对于t 1; a t; b 2;可以用如下的伪汇编表示 1: Loadi reg3, 1; # 将立即数1放入寄存器reg3 2Move reg4, reg3; # 将reg3的数据放入reg4 3: Store reg4, a; # 将寄存器reg4中的数据存入内存地址a4: Loadi reg5, 2; # 将立即数2放入寄存器reg5 5: Store reg5, b; # 将寄存器reg5中的数据存入内存地址b 通常情况下应该按照12345顺序执行这样的内存模型称为强顺序的。这时a的赋值始终先于b的赋值执行。 但是我们可以看到指令123与指令45毫无关联因此一些处理器可能就会重排指令顺序比如14253的顺序执行。这种场景我们称为弱顺序的b的赋值也就会先于a的赋值。 而在多线程中强顺序意味着多个线程看到的指令执行顺序是一致的且反馈到处理器层面内存数据变换顺序与指令顺序一致而弱顺序则无法保证这一点。 而原子操作要求都是顺序的这在强顺序内存模型下是不需要额外处理的而对于弱顺序内存模型下则需要添加内存栅栏这样的指令来确保顺序一致性这对性能往往有较大的损耗。 3.内存顺序memory_order 以上描述的都是硬件上的一些内存模型而C11引入的内存模型和顺序一致性则是针对编译器而言 编译器保证原子操作的指令间顺序不变即保证产生的读写原子类型的变量的机器指令与代码编写者看到的是一致的。处理器对原子操作的汇编指令的执行顺序不变。这对于x86这样的强顺序的体系结构而言并没有任何的问题而对于PowerPC这样的弱顺序的体系结构而言则要求编译器在每次原子操作后加入内存栅栏。 C11的原子操作默认都是顺序一致性的这对强顺序体系而言没有影响但是对于弱顺序体系而言添加内存栅栏来确保顺序一致性会大大增加性能消耗。为了解决这一问题C11引入了内存顺序memory_order的概念即对所有的原子操作提供一个参数入口传入不同的momery_order以弱化对顺序一致性的要求。 具体使用如下 #include thread #include atomic #include iostream using namespace std; atomicint a {0}; atomicint b {0}; int ValueSet(int) {int t 1;a.store(t, memory_order_relaxed);b.store(2, memory_order_relaxed); } int Observer(int) {cout ( a , b ) endl; // 可能有多种输出 } int main() {thread t1(ValueSet, 0);thread t2(Observer, 0);t1.join();t2.join();cout Got ( a , b ) endl; // Got (1, 2)return 0; } // 编译选项:g -stdc11 6-3-6.cpp -lpthreada和b的赋值操作传入参数memory_order_relaxed表示对执行顺序不做任何要求从而放开顺序一致性。 memory_order的枚举值有 通常情况下我们可以把atomic成员函数可使用的memory_order值分为以下3组 ❑ 原子存储操作store可以使用memorey_order_relaxed、memory_order_release、memory_order_seq_cst。 ❑ 原子读取操作load可以使用memorey_order_relaxed、memory_order_consume、memory_order_acquire、memory_order_seq_cst。 ❑ RMW操作read-modify-write即一些需要同时读写的操作比如之前提过的atomic_flag类型的test_and_set()操作。又比如atomic类模板的atomic_compare_exchange()操作等都是需要同时读写的。RMW操作可以使用memorey_order_relaxed、memory_order_consume、memory_order_acquire、memory_order_release、memory_order_acq_rel、memory_order_seq_cst。 排除顺序一致和松散两种方式我们能不能保证程序“既快又对”地运行呢实际上我们所需要的只是a.store先于b.store发生b.load先于a.load发生的顺序。这要这两个“先于发生”关系得到了遵守对于整个程序而言来说就不会发生线程间的错误。所以我们可以修改代码如下 #include thread #include atomic #include iostream using namespace std; atomicint a; atomicint b; int Thread1(int) {int t 1;a.store(t, memory_order_relaxed);b.store(2, memory_order_release); // 本原子操作前所有的写原子操作必须完成 } int Thread2(int) {while(b.load(memory_order_acquire) ! 2); // 本原子操作必须完成才能执行之后所有的读原子操作cout a.load(memory_order_relaxed) endl; // 1 } int main() {thread t1(Thread1, 0);thread t2(Thread2, 0);t1.join();t2.join();return 0; } // 编译选项:g -stdc11 6-3-8.cpp -lpthreadb.store采用了memory_order_release内存顺序这保证了本原子操作前所有的写原子操作必须完成也即a.store操作必须发生于b.store之前。b.load采用了memory_order_acquire作为内存顺序这保证了本原子操作必须完成才能执行之后所有的读原子操作。即b.load必须发生在a.load操作之前。这样一来通过确立“先于发生”关系的我们就完全保证了代码运行的正确性即当b的值为2的时候a的值也确定地为1。
http://www.yutouwan.com/news/384370/

相关文章:

  • 微信怎么开团购卖东西谷歌seo知识
  • 如何做学校的网站如何破解wordpress数据库
  • 建立个人网站网络营销是什么整体营销战略的一个组成部分
  • cf小号自助购买网站求2021没封的良心网站
  • 有哪些网站教做吃的oa网站开发模板
  • 中山快速建站合作网站设计网站源码
  • 网站建设实训意义软件编程培训机构
  • 长春网站建设招代理建设电子商务网站考核试卷
  • 常用的网站推广广告设计公司需要用专线网吗
  • 设计logo网站 生成器网站图片加水印
  • 个人内网网站建设百度pc网页版入口
  • 广宁网站建设公司小票在线生成小程序
  • 有哪些可以做策划方案的网站专业做生鲜的网站
  • 江山市建设局网站汕头网站建设制作厂家
  • 江苏高端品牌网站建设教做世界美食的网站
  • 做网站需要什么基础网站源码绑定域名
  • 网站注册的账号怎么注销互联网技术服务
  • 青岛营销型网站网站每年的维护费
  • 网站模版怎么上传到空间电商网站建设存在哪些问题
  • 平度市建设局网站闵行网站建设外包
  • 网站建设参考网站的说明书wordpress4.4.2下载
  • 最好的ppt模板网站湖州网站建设方案
  • 美妆网站怎么做网站设计总结与心得体会
  • 进一步加强区门户网站建设管理最好网站建设制作是那个
  • 招商局网站建设管理总结外贸网站好做吗
  • 邢台做网站哪个网络公司好wordpress主题修改菜鸟教程
  • 营销型网站建设费用怎么这么大html静态网页制作案例
  • 广州继续教育平台登录入口网页优化哪家公司做得好
  • 网站开发要学的代码海南企业网站做优化排名
  • 简单网站建设费用网页传奇开服表