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

网站建设哪家有精准防恶意点击软件

网站建设哪家有,精准防恶意点击软件,怎么重新网站做301,深圳市住房建设部官方网站目录 11.1 与并发相关的错误类型 11.1.1 不必要的阻塞 11.1.2 条件竞争 11.2 定位并发错误的技术 11.2.1 代码审阅——发现潜在的错误 11.2.2 通过测试定位并发相关的错误 11.2.3 可测试性设计 11.2.4 多线程测试技术 11.2.5 构建多线程测试代码 11.2.6 测试多线程代…

 

目录

11.1 与并发相关的错误类型

11.1.1 不必要的阻塞

11.1.2 条件竞争

11.2 定位并发错误的技术

11.2.1 代码审阅——发现潜在的错误

11.2.2 通过测试定位并发相关的错误

11.2.3 可测试性设计

11.2.4 多线程测试技术

11.2.5 构建多线程测试代码

11.2.6 测试多线程代码性能


参考《C++ 并发编程实战》 著:Anthony Williams 译: 吴天明

11.1 与并发相关的错误类型

有一些错误与使用并发直接相关,本章重点关注这些错误。通常,并发相关的错误通常有两大类:
不必要的阻塞
条件竞争

11.1.1 不必要的阻塞

这个主题可以分成以下几个问题:
死锁——如在第3章所见,死锁的情况下,两个线程会互相等待。当线程产生死锁,应该完成的任务就会持续搁置。举个例子来说,一些线程是负责对用户界面操作的线程,在死锁的情况下,用户界面就会无响应。另一些例子中,界面接口会保持响应,不过有些任务就无法完成,比如:查询无结果返回或文档未打印。
活锁——与死锁的情况类似。不同的地方在于线程不是阻塞等待,而是在循环中持续检查,例如:自旋锁。比较严重的情况下,其表现和死锁一样(应用不会做任何处理,停止响应),CPU的使用率还居高不下;因为线程还在循环中被检查,而不是阻塞等待。不太严重的情况下,因为使用随机调度,活锁的问题还是可以解决的。
I/O阻塞或外部输入——当线程被外部输入所阻塞,线程也就不能做其他事情了(即使,等待输入的情况永远不会发生)。因此,被外部输入所阻塞,就会让人不太高兴,因为可能有其他线程正在等待这个线程完成某些任务。

11.1.2 条件竞争

条件竞争经常会产生以下几种类型的错误:
数据竞争——因为未同步访问一块共享内存,将会导致代码产生未定义行为。第5章已经介绍了数据竞争,也了解了C++的内存模型。数据竞争通常发生在错误的使用原子操作上,做同步线程的时候,或没使用互斥量保护共享数据的时候。
破坏不变量——主要表现为悬空指针(因为其他线程已经将要访问的数据删除了),随机存储错误(因为局部更新,导致线程读取了不一样的数据),以及双重释放(比如:当两个线程对同一个队列同时执行pop操作,想要删除同一个关联数据),等等。不变量被破坏可以看作为“基于数据”的问题。当独立线程需要以一定顺序执行某些操作时,错误的同步会导致条件竞争,比如:顺序被破坏。
生命周期问题——虽然这类问题也能归结为破坏了不变量,不过这里将其作为一个单独的类别给出。这里的问题是线程会访问不存在的数据,这可能是因为数据被删除或销毁了,或者转移到其他对象中去了。生命周期问题,通常是在一个线程引用了局部变量,在线程还没有完成前,局部变量的“死期”就已经到了,不过这个问题并不止存在这种情况下。当手动调用join()等待线程完成工作,需要保证异常抛出的时候,join()还会等待其他未完成工作的线程。这是线程中基本异常安全的应用。

11.2 定位并发错误的技术

11.2.1 代码审阅——发现潜在的错误

如果让你的同事来审阅代码,他/她肯定对你的代码不是很熟悉。因此,他/她会从不同的角度来看你的代码,然后指出你没有注意的事情。如果你的同事都没有空,你可以叫朋友,或传到网络上,让网友审阅(注意,别传一些机密代码上去)。实在没有人审阅,不要着急。对于初学者,可以将代码放置一段时间——先去做应用的另外的部分,或是阅读一本书籍,亦或出去溜达溜达。在休息之后,当再集中注意力做某些事情(潜意识会考虑很多问题)。同样,当你做完其他事情,回头再看这段代码,就会有些陌生——你可能会从另一个角度来看你自己以前写的代码。
另一种方式就是自己审阅。可以向别人详细的介绍你所写的功能,可能并不是一个真正的人——可能要对玩具熊或橡皮鸡来进行解释,并且我个人觉得写一些比较详细的注释是非常有益的。在解释过程中,会考虑每一行过后,会发生什么事情,有哪些数据被访问了,等等。问自己关于代码的问题,并且向自己解释这些问题。我觉得这是种非常有效的技巧——通过自问自答,对每个问题认真考虑,这些问题往往都会揭示一些问题,也会有益于任何形式的代码审阅。

审阅多线程代码需要考虑的问题
并发访问时,哪些数据需要保护?
如何确定访问数据受到了保护?
是否会有多个线程同时访问这段代码?
这个线程获取了哪个互斥量?

其他线程可能获取哪些互斥量?
两个线程间的操作是否有依赖关系?如何满足这种关系?
这个线程加载的数据还是合法数据吗?数据是否被其他线程修改过?
当假设其他线程可以对数据进行修改,这将意味着什么?并且,怎么确保这样的事情不会发生?
我最喜欢最后一个问题,因为它让我去考虑线程之间的关系。通过假设一个bug和一行代码相关联,你就可以扮演侦探来追踪bug出现的原因。为了让你自己确定代码里面没有bug,需要考虑代码运行的各种情况。数据被多个互斥量所保护时,这种方式尤其有用,比如:使用线程安全队列(第6章),可以对队头和队尾使用独立的互斥量:就是为了确保在持有一个互斥量的时候,访问是安全的,必须保持有其他互斥量的线程不能同时访问同一元素。还需要特别关注的是,对公共数据的显式处理,使用一个指针或引用的方式来获取数据。
倒数第二个问题也很重要,因为这里很容易产生错误:先释放再获取一个互斥量的前提是,其他线程可能会修改共享数据。虽然很明显,但当互斥锁不是立即可见——可能因为是内部对象——就会不知不觉的掉入陷阱中。第6章已经了解到这种情况是怎么引起条件竞争的,以及如何给细粒度线程安全数据结构带来麻烦的。不过,非线程安全栈将top()和pop()操作分开是有意义的,当多线程并发的访问这个栈,问题会马上出现,因为在两个操作的调用间,内部互斥锁已经被释放,并且另一个线程对栈进行了修改。解决方案就是将两个操作合并,就能用同一个锁来对操作的执行进行保护,就消除了条件竞争的问题。

11.2.2 通过测试定位并发相关的错误

11.2.3 可测试性设计

测试多线程代码很困难,所以需要将其变得简单一些。很重要的一件事就是设计代码时,考虑其的可测试性。可测试的单线程代码设计已经说烂了,而且其中许多建议现在依旧适用。通常,如果代码满足一下几点,就很容易进行测试:

每个函数和类的关系都很清楚。
函数短小精悍。
测试用例可以完全控制被测试代码周边的环境。
执行特定操作的代码应该集中测试,而非分布式测试。

需要在完成编写后,考虑如何进行测试。
以上这些在多线程代码中依旧适用。实际上,我会认为对多线程代码的可测试性要比单线程的更为重要,因为多线程的情况更加复杂。最后一个因素尤为重要:即使不在写完代码后,去写测试用例,这也是一个很好的建议,能让你在写代码之前,想想应该怎么去测试它——用什么作为输入,什么情况看起来会让结果变得糟糕,以及如何激发代码中潜在的问题,等等。

并发代码测试的一种最好的方式:去并发化测试。如果代码在线程间的通讯路径上出现问,就可以让一个已通讯的单线程进行执行,这样会减小问题的难度。在对数据进行访问的应用进行测试时,可以使用单线程的方式进行。这样线程通讯和对特定数据块进行访问时只有一个线程,就达到了更容易测试的目的。

例如,当应用设计为一个多线程状态机时,可以将其分为若干块。将每个逻辑状态分开,就能保证对于每个可能的输入事件、转换或其他操作的结果是正确的;这就是单线程测试的技巧,测试用例提供的输入事件将来自于其他线程。之后,核心状态机和消息路由的代码,就能保证时间能以正确的顺序,正确的传递给可单独测试的线程上,不过对于多并发线程,需要为测试专门设计简单的逻辑状态。
或者,如果将代码分割成多个块(比如:读共享数据/变换数据/更新共享数据),就能使用单线程来测试变换数据的部分。麻烦的多线程测试问题,转换成单线程测试读和更新共享数据,就会简单许多。某些库会用其内部变量存储状态时需要小心,当多线程使用同一库中的函数,这个状态就会被共享。这的确是一个问题,并且问题不会马上出现在访问共享数据的代码中。不过,随着你对这个库的熟悉,就会清楚这样的情况会在什么时候出现。之后,可以适当的加一些保护和同步或使用B计划——让多线程安全并发访问的功能。

11.2.4 多线程测试技术

蛮力测试
代码有问题的时候,就要求蛮力测试一定能看到这个错误。这就意味着代码要运行很多遍,可能会有很多线程在同一时间运行。要是有bug出现,只能线程出现特殊调度的时候;代码运行次数的增加,就意味着bug出现的次数会增多。
当有几次代码测试通过,你可能会对代码的正确性有一些信心。如果连续运行10次都通过,你就会更有信心。如果你运行十亿次都通过了,那么你就会认为这段代码没有问题了。
自信的来源是每次测试的结果。如果你的测试粒度很细,就像测试之前的线程安全队列,那么蛮力测试会让你对这段代码持有高度的自信。另一方面,当测试对象体积较大的时候,调度序列将会很长,即使运行了十亿次测试用例,也不让你对这段代码产生什么信心。

组合仿真测试
名字比较口语化,我需要解释一下这个测试是什么意思:使用一种特殊的软件,用来模拟代码运行的真实情况。你应该知道这种软件,能让一台物理机上运行多个虚拟环境或系统环境,而硬件环境则由监控软件来完成。除了环境是模拟的以外,模拟软件会记录对数据序列访问,上锁,以及对每个线程的原子操作。然后使用C++内存模型的规则,重复的运行,从而识别条件竞争和死锁。
虽然,这种组合测试可以保证所有与系统相关的问题都会被找到,不过过于零碎的程序将会在这种测试中耗费太长时间,因为组合数目和执行的操作数量将会随线程的增多呈指数增长态势。这个测试最好留给需要细粒度测试的代码段,而非整个应用。另一个缺点就是,代码对操作的处理,往往会依赖与模拟软件的可用性。
所以,测试需要在正常情况下,运行很多次,不过这样可能会错过一些问题;也可以在一些特殊情况下运行多次,不过这样更像是为了验证某些问题。

使用专用库对代码进行测试
虽然,这个选择不会像组合仿真的方式提供彻底的检查,不过可以通过特别实现的库(使用同步原语)来发现一些问题,比如:互斥量,锁和条件变量。例如,访问某块公共数据的时候,就要将指定的互斥量上锁。数据被访问后,发现一些互斥量已经上锁,就需要确定相关的互斥量是否被访问线程锁住;如果没有,测试库将报告这个错误。当需要测试库对某块代码进行检查时,可以对相应的共享数据进行标记。
多个互斥量同时被一个线程持有时,测试库也会对锁的序列进行记录。如果其他线程以不同的顺序进行上锁,即使在运行的时候测试用例没有发生死锁,测试库都会将这个行为记录为“有潜在死锁”可能。
测试多线程代码时,另一种库可能会用到,以线程原语实现的库,比如:互斥量和条件变量;当多线程代码在等待,或是被条件变量通过notify_one()提醒的某个线程,测试者可以通过线程,获取到锁。就可以让你来安排一些特殊的情况,以验证代码是否会在这些特定的环境下产生期望的结果。
C++标准库实现中,某些测试工具已经存在于标准库中,没有实现的测试工具,可以基于标准库进行实现。

11.2.5 构建多线程测试代码

在特定时间内,需要安排一系列线程,同时去执行指定的代码段。两个线程的情况,就很容易扩展到多个线程。
首先,需要知道每个测试的不同之处:
环境布置代码,必须首先执行
线程设置代码,需要在每个线程上执行
线程上执行的代码,需要有并发性
并发执行结束后,后续代码需要对代码的状态进行断言检查

了解了各个代码块,就需要保证所有事情按计划进行。一种方式是使用一组使用承诺值来表示是否准备好,然后让std::promise等待(复制)一个std::promise std::shared_future来表示就绪状态。每个线程;主线程会等待每个线程上的承诺值设置后,才按下“开始”键。这就能保证每个线程能够同时开始,并且在准备代码执行完成后,并发代码就可以开始执行了;任何线程的特定设置都需要在设置承诺值前完成。最终,主线程会等待所有线程完成,并且检查其最终状态。还需要格外关心异常,所有线程在准备好的情况下,再按下“开始”键;否则,未准备好的线程就不会运行。

清单11.1 对一个队列并发调用push()和pop()的测试用例

1.void test_concurrent_push_and_pop_on_empty_queue()
2.{
threadsafe_queue<int> q;
3.
// 1
4.
5.std::promise<void> go,push_ready,pop_ready;
6.std::shared_future<void> ready(go.get_future());
// 2
// 3
7.
8.std::future<void> push_done;
9.std::future<int> pop_done;
// 4
10.
11.try
12.{
push_done=std::async(std::launch::async,
13.
// 5
14.[&q,ready,&push_ready]()
15.{
16.push_ready.set_value();
17.ready.wait();
q.push(42);
18.
}
19.
);
20.
pop_done=std::async(std::launch::async,
21.
// 6
22.[&q,ready,&pop_ready]()
23.{
24.pop_ready.set_value();
25.ready.wait();
26.return q.pop();
// 7
}
27.
);
28.
29.push_ready.get_future().wait();
30.pop_ready.get_future().wait();
31.go.set_value();
// 9
33.push_done.get();
// 10
34.assert(pop_done.get()==42);
// 8
32.
assert(q.empty());
35.
36.}
37.catch(...)
38.{
go.set_value();
39.
// 12
throw;
40.
}
41.
42.
// 11
}

11.2.6 测试多线程代码性能


选择以并发的方式开发应用,就是为了能够使用日益增长的处理器数量;通过处理器数量的增加,来提升应用的执行效率。因此,确定性能是否有真正的提高就很重要了(就像其他优化一样)。
并发效率中有个特别的问题——可扩展性——你希望代码能很快的运行24次,或在24芯的机器上对数据进行24(或更多)次处理,或其他等价情况。如8.4.2节中所述,当有重要的代码以单线程方式运行时,就会限制性能的提高。因此,在做测试之前,回顾一下代码的设计结构是很有必要的;这样就能判断,代码在24芯的机器上时,性能会不会提高24倍,或是因为有串行部分的存在,最大的加速比只有3。
对数据访问时,处理器之间会有竞争,会对性能有很大的影响。需要合理的权衡性能和处理器的数量,处理器数量太少,就会等待很久;处理器过多,又会因为竞争的原因等待很久。
因此,在对应的系统上通过不同的配置,检查多线程的性能就很有必要,这样可以得到一张性能图。最起码(如果条件允许)需要在一个单处理器的系统上和一个多处理核芯的系统上进行测试。

http://www.hrbkazy.com/news/48247.html

相关文章:

  • 购物网站二级页面模板外贸订单怎样去寻找
  • 网站 建设网片
  • 申报湖南创新型省份建设专项网站上海优化seo排名
  • 外贸网站教程网站推广网站
  • 手机手机端网站建设个人网站免费推广
  • 网站后台管理员扫描市场营销策划
  • 浦口做网站价格360竞价推广客服电话
  • 做网站视频教程鹤壁网站推广公司
  • 怎么做网站给国外看见网络广告营销经典案例
  • 自己做网站咋做自媒体seo是什么意思
  • 网页设计的特点有哪些长沙网站优化推广
  • 做西点的网站游戏代理加盟平台
  • 田阳县建设局网站一个具体网站的seo优化方案
  • 如何修改网站域名google关键词搜索量
  • 网站建设设计咨询新乡网络推广外包
  • 办文明网站做文明网民活动方案营销策略有哪些方面
  • 建站网络建立科技开发怎么提高seo关键词排名
  • 萧山工程建设有限公司网站新品推广活动方案
  • php 个人网站建立网站的详细步骤
  • 做别人的网站诈骗视频下载怎么制作seo搜索优化
  • 中国林业建设协会网站关键词汇总
  • 日照住房和城乡建设局网站seo网址大全
  • 用axure怎么做h5网站网站批量收录
  • 美甲网站自适应源码泰安seo网络公司
  • 开锁在百度上做网站要钱吗深圳网站关键词优化公司
  • 七星彩投注网站建设安装百度一下
  • 做网站前产品经理要了解什么什么是优化
  • 久久建筑网会员登陆aso优化分析
  • 网站高质量外链中山网站建设公司
  • 旅游网站建设需求分析哈尔滨网络优化公司有哪些