经过几天的纠结之后,终于在今天把该环境搭建起来了,在这里要特别感谢深圳-乙醇老师的帮助
搭建环境:
系统环境:Win7 64位;
JDK版本:java version “1.6.0_45″
eclipse版本:4.2.0
下面就给大家介绍一下详细的步骤:
(1) 下载一个active-python安装软件(该软件已经包含了python2.7和setuptools),默认安装好之后,对应的
python目录里面就已经存在Scripts文件夹了,十分方便
(2) 添加Path,比如:C:\Python27;C:\Python27\Scripts;(最好添加当前用户下面的path,避免破坏其他用户的系统环境)
(3) 利用pip安装selenium,具体做法如下:
a. 下载和安装一个帆樯软件,这里给大家介绍自由门,该软件无需安装,只需要运行起exe文件,就可以,很方便快捷
b. 进入dos模式,切换路径到C:\Python27\Scripts,然后输入命令pip install selenium,系统就会自动下载和安装selenium
(4) 打开eclipse,安装PyDev插件,具体操作如下:
a. 直接在Eclipse中选择菜单:Help—Install New Software..—Add,输入http://pydev.org/updates,下载并安装。
b. 完成后重启Eclipse,在Eclipse菜单Help->About Eclipse->Installation Detail->Plug-ins,若能看到PyDev组件,则表示安装成功
(5) 需要配置Python解释器,具体操作如下:
在 Eclipse 菜单栏中,选择 Window > Preferences > Pydev > Interpreter – Python。单击 New,选择 Python 解释器 python.exe,点击ok之后,就能添加你需要的插件内容
(6) 测试是否配置成功
a. 新建一个python项目,操作步骤可见截图
b. 新建一个python module,输入以下内容
#-*- conding=utf-8 -*- from selenium import webdriver if __name__ == "__main__": driver = webdriver.Firefox() driver.implicitly_wait(30) driver.get("http://www.google.com.hk") print 'Page title is:',driver.title driver.quit() |
c.如果通过firefox浏览器打开了google界面,那么表明配置成功
其中需要提示一下:
Win 7 64位系统环境下面搭建该测试环境,如果你是先安装python2.7之后再来安装setuptools和pip,那么你在用pip install selenium时可能会报错,比如提示你:Storing debug log for failure in C:\Users\XXX\pip\pip.log,所以需要在任意一个根目录下面新建一个register.py,该文件的具体内容,如下:
# # script to register Python 2.0 or later for use with win32all # and other extensions that require Python registry settings # # written by Joakim Loew for Secret Labs AB / PythonWare # # source: # http://www.pythonware.com/products/works/articles/regpy20.htm # # modified by Valentine Gogichashvili as described in http://www.mail-archive.com/distutils-sig@python.org/msg10512.html import sys from _winreg import * # tweak as necessary version = sys.version[:3] installpath = sys.prefix regpath = "SOFTWARE\\Python\\Pythoncore\\%s\\" % (version) installkey = "InstallPath" pythonkey = "PythonPath" pythonpath = "%s;%s\\Lib\\;%s\\DLLs\\" % ( installpath, installpath, installpath ) def RegisterPy(): try: reg = OpenKey(HKEY_CURRENT_USER, regpath) except EnvironmentError as e: try: reg = CreateKey(HKEY_CURRENT_USER, regpath) SetValue(reg, installkey, REG_SZ, installpath) SetValue(reg, pythonkey, REG_SZ, pythonpath) CloseKey(reg) except: print "*** Unable to register!" return print "--- Python", version, "is now registered!" return if (QueryValue(reg, installkey) == installpath and QueryValue(reg, pythonkey) == pythonpath): CloseKey(reg) print "=== Python", version, "is already registered!" return CloseKey(reg) print "*** Unable to register!" print "*** You probably have another Python installation!" if __name__ == "__main__": RegisterPy() |
建立好之后,在dos模式下,进入到对应的根目录下,输入以下命令:python register.py,系统就会自动运行该文件。然后再来运行pip来下载安装selenium,就不会报错了,报错的童鞋可以试试,嘿嘿~~
最近在做
单元测试相关的东西,有一些想法,也研究实现了一部分,和大家分享。
单元测试的用例要和代码分离,采用xml的格式。
每一个单元测试类对应一个xml,采用如下格式:
<class name="com.xxx.service.test.DemoServiceTest"> <beforeClass></beforeClass> <afterClass></afterClass> <method name="testGetToken"> <before></before> <after></after> <!-- 输入参数类型错误 --> <case name="err_001"> <!-- 方法参数可能有多个 --> <request> <param type="com.xxxi.domain.request.TokenRequest"> <merchantId>33333</merchantId> <signType>MD5</signType> <businessType>1001</businessType> <outTradeNo>4222222222</outTradeNo> </param> </request> <!-- 方法期望返回值 --> <response type="com.xxx.domain.response.TokenResponse"> <responseCode>0</responseCode> <tradeNo>831</tradeNo> </response> </case> <case name="case_002"> <request> <param type="com.xxx.request.TokenRequest"> <merchantId>33333</merchantId> <signType>MD5</signType> <businessType>1001</businessType> <outTradeNo>4222222222</outTradeNo> </param> </request> <!-- 方法期望返回值 --> <response type="com.elong.pb.payment.api.domain.response.TokenResponse"> <responseCode>0</responseCode> <tradeNo>831</tradeNo> </response> </case> </method> </class> |
根节点为单元测试类的类名
类下面是多个<method>节点
每个<method>节点下有多个<case>节点
每个<case>节点下有<request>和<response>分别为输入参数和期望返回结果
输入参数可能有多个,故<request>节点下可能有多个<param>节点
以上是对xml设计的介绍。
下面讲实现。
通过一些实际使用,发现JUnit对参数化的支持非常不好。对于参数化测试有很大的局限性,参数必须定义成类的成员变量,这样对于一个类中有多个测试方法造成很大的不便。JUnit的参数化测试只适合一个类只有一个测试方法的情况。
于是我用了TestNG,TestNG也是一个(但并不仅仅是)单元测试框架,与JUnit类似,有类似的生命周期和类似的用法。但TestNG对参数化测试支持的很好:
public class TestNGTest { @DataProvider(name = "test2") public Object[][] createTest2() { return new Object[][] { { "input1", "expect" }, { "input2", "expect2" } }; } } @Test(dataProvider = "test2") public void test2(String input, String expect) { System.err.println(input+" "+expect); } } |
TestNG的每个测试方法是可以有参数的,有一个数据提供者(dataProvider注解),dataProvider和每个test是一一对应的关系。这样是方法级别的(而JUnit是类级别)
DataProvider会返回一个二维数组。剩下的事情就是如何读取解析上面的xml,并生成二维数组返回了。
(好像TestNG也有一个xml配置,配置DataProvider,但感觉不怎么样,可能只支持简单类型,不利于自己定制)
对web controller的测试也与之类似,springmvc有mockMvc,在此不过多介绍了。
以上。
实际开发中,开发
android软件的过程需要不断的进行
测试。使用
Junit测试框架,是正规android开发的必用技术,在Junit中可以得到组件,可以模拟发送时间和检测程序处理的正确性。使用方法如下:
第一步:添加单元测试所使用到的类库以及单元测试的运行工具
在application结点内部添加
<uses-library android:name=”android.test.runner”/> |
在application结点外部添加
<instrumentation android:name=”android.test.InstrumentationTestRunner” Android:targetPackage=”cn.itcast.action”android:label=” Test for My App”/> |
上面的targetPackage就是要测试的目标应用的包名
第二步:编写单元测试类代码,选择要测试的方法,右键点击”Run As”-“Android JUnit Test”来进行测试,注意该类需要继承AndroidTestCase类
等理解了这两个概念后,想想也挺容易理解,之前一直被这个困惑。
“运行时间设置”的时间是 LR会持续连续运行脚本的时间,运行次数可以在LR中查看。
“迭代次数设置”的次数 就是以脚本为主,设置了多少次,脚本就会运行多少次;此时运行时间一般设置为运行完成。
如果同时设置迭代次数和持续时间, 那么当时间结束后, 不管迭代次数是否已经到达设定值,此虚拟用户都会结束。
迭代次数的设置和运行时间的长短都是为了让
测试能够运行一段时间,获得系统各性能指标的一个平均值。如果不是稳定性测试,一般的
性能测试我都设置为10-60分钟之间这里的这个时间是所有用户都加载上运行的一个时间,即是选择了“所有用户都init以后再运行”的duration时间。
MySQL数据库的默认编码已经设置为utf8了,在python程序内也已经把字符串的编码转换成了utf8,可是通过MySQLdb插入数据库后,显示的还是一堆乱码。
后来发现是建立数据库连接时的问题:
conn = MySQLdb.connection(host="localhost", user="root", passwd="password", use_unicode=True, charset="utf8") |
之前在获得数据库连接时,没有写后面红色部分两个参数,以为数据库默认编码和字符串编码都为utf8,就能正确插入为utf8的字符串了。
添加了红色部分的参数后,正常插入,问题解决。
P.S. 在插入数据库时一定要注意字符串中的单引号,因为MySQL数据库中单引号为特殊字符,若原始数据中含有单引号,必须连写两个才能正确插入。如 Aaron's book, 插入数据库时应写为 Aaron''s book
字体: 小 中 大 | 上一篇 下一篇 | 打印 | 我要投稿 | 推荐标签: Bug 苹果 测试技术
近日,
苹果向iOS用户推送了一个安全更新,指出在iOS系统中SSL/TLS安全连接存在严重的
bug,但并没有给出更详细的说明。对此问题的解答已经出现在Hacker News的头条,我想大家都已经知道了这个漏洞,也不需要再胡乱猜测了。
以下就是导致这个bug的一段代码:
static OSStatus SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams, uint8_t *signature, UInt16 signatureLen) { OSStatus err; ... if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail; ... fail: SSLFreeBuffer(&signedHashes); SSLFreeBuffer(&hashCtx); return err; } |
注意其中有两个连续的go to fail语句,第一个会正确地在if判断为真时执行,但是第二个却在任意情况下都会执行,尽管它有着看似标准的语句缩进。于是当代码跳转至fail时,由于认证使用的final方法还未执行,而update方法执行成功,因此err会包含一个校验成功的信息,导致对签名的认证永远不会失败。
认证签名时将会检测ServerKeyExchange消息中的签名,它用于DHE和ECDHE加密套件(多种加密算法联合使用)在建立连接时获取会话密钥(ephemeral key,本次会话的临时密钥)。服务器告诉客户端:“这是给你的会话密钥和签名,通过我的证书,你可以确定密钥和签名是来自于我的。”而现在会话密钥和证书链之间的关联已经断裂,所有曾经安全的认证都不再有效。这意味着,服务器可以向客户端发送正确的证书链,但在连接握手的过程中使用错误的私钥进行签名甚至干脆不签名,因为我们无法确认这个服务器是否持有对应此证书的正确的私钥。
这个Bug出现在SecureTransport的代码中,它将影响iOS的某个早期版本直到7.0.6(其中7.0.4我已经确认过),同时也会影响OS X系统(在10.9.1上已经得到确认)。所有使用了SecureTransport的地方都会被波及到,也就等于是绝大部分苹果系统上的软件。Chrome和Firefox在SSL/TLS连接中使用的NSS,因此得以幸免。然而如果你的软件更新程序使用了SecureTransport,那么前面的讨论都不能说明什么了。(译注:更新程序可能连接到仿冒主机。)
对此我构建了一个简单的
测试网站:https://www.imperialviolet.org:1266。注意端口号(1226是这个漏洞在CVE里的编号),443端口运行着一个正常的网站,而1226端口的网站将会发送使用错误私钥签名的证书。如果你使用https连接去访问,就能够重现这个bug。
即使证书链是正确的,由于它和连接握手之间的关联已经被破坏,我认为任何形式的证书锁定都无法阻止这种错误的认证。同时,这个bug不仅仅影响使用DHE或者ECDHE加密套件的网站,因为攻击者可以自行选择合适的加密套件。
在TLS 1.2的针对ServerKeyExchange消息的认证是使用的另一个方法,因此没有受到影响。但仍然有上面提到的问题,攻击者可以选择任何客户端能够使用的版本。当然如果客户端仅支持TLS 1.2,那就完全没有问题了。客户端也可以只使用明文-RSA加密套件,那么就不存在ServerKeyExchange消息,同样起到了防范的效果。(当然在这两种方法中,前一种更加可取。)
根据我的测试发现,iOS 7.0.6已经修复了这个问题,但是在OS X 10.9.1中仍然存在。(补充:好像这个bug在OS X系统中是在10.9版引入的,但是iOS6的某些版本中就早已出现了。iOS 6.1.6昨天修复了此bug。)这样潜伏在代码深处的bug简直就是一个噩梦。我相信这仅仅是个失误,但无论是谁手滑(手贱)把这样的代码写出来,我都为他感到深切的悲痛。
下面是一段和这个bug有相同问题的代码:
extern int f(); int g() { int ret = 1; goto out; ret = f(); out: return ret; } |
如果我编译时候加上参数-Wall(启用所有警告),在Xcode中无论是GCC 4.8.2还是Clang 3.3都没有对死代码发出警告,对此我非常惊讶。更好的编译警告本可以阻止这样的悲剧,又或许在实际编码中这类警告发生第一类错误(弃真错误)的概率太高?(感谢Peter Nelson指出在Clang可以使用-Wunreachable-code参数对这样的问题发出警告,而不是-Wall。)
看起来是允许if代码块不使用大括号才导致了这样的代码风格,但是有人在大括号里也可能使用错误的代码缩进,因此我也没觉得大括号带来了多少便利。
写一个测试用例本可以发现这个问题,但是由于它深嵌在连接握手的过程中,所以非常复杂。这个用例需要写一个完全独立的TLS栈,并且包含大量发送无效握手请求的配置。在Chromium中我们有个修改过的TLSLite工具可以做类似的测试,但是我不太记得我们的用例是否完全适用于这个bug的情况。(如果不行的话,听起来好像我已经知道周一早上要干嘛了)(译注:当然是把用例改到可以完全适用)
代码审查对发现这类bug非常有效。不仅仅是浏览审阅,而是审查每句新写的代码。我不知道苹果一般怎么做代码审查,但是我充分相信我的同事,Wan-Teh和Ryan Sleevi。如果我意外手滑,他们一定会及时发现。可惜不是每个人都有幸和这样的同事一起工作。
最近,针对苹果忽略对证书中主机的校验这个事情,有一系列讨论。的确在OS X中使用curl时,即使IP地址不在证书中,命令行也会接受和这个主机的连接。然而我没找到更多问题了,Safari也没有受到影响。
Java语言中,中文字符所占的字节数取决于字符的编码方式,一般情况下,采用ISO8859-1编码方式时,一个中文字符与一个英文字符一样只占1个字节;采用GB2312或GBK编码方式时,一个中文字符占2个字节;而采用UTF-8编码方式时,一个中文字符会占3个字节。我们可以通过String类的getBytes(String charsetName)方法来获取到字符串以指定编码方式编码后所得到的字节数组,然后字节数组的长度就是该字符串在指定编码方式下所占的字节数。下面为一个
测试示例:
public static void main(String []args) throws UnsupportedEncodingException { // 运行结果:2 System.out.println("测试".getBytes("ISO8859-1").length); // 运行结果:4 System.out.println("测试".getBytes("GB2312").length); // 运行结果:4 System.out.println("测试".getBytes("GBK").length); // 运行结果:6 System.out.println("测试".getBytes("UTF-8").length); } |
注意:String类的不带参数的getBytes()方法会以程序所运行平台的默认编码方式为准来进行转换,在不同平台下就会有不同的结果,因此建议使用指定编码方式的getBytes(String charsetName)方法。
引言
当前主流的软件生命周期模型有瀑布模型、原型模型、螺旋模型、增量模型、渐进模型、快速
软件开发以及
Rational统一过程等,但是在这些模型中,
软件测试的价值并未得以足够的体现,也没有给软件测试以足够的重视,利用这些模型无法更好的指导测试
工作。本文对软件测试模型做了循序渐进的剖析,可以让测试相关工作者能够对软件测试模型能够有个较为深入的认识。
二、模型解读
1.V模型
在软件测试方面,V模型是最广为人知的模型,他是软件开发瀑布模型的变种,V模型是在快速应用开发(RapApplicationDevelopment,RAD)模型基础上演变而来,由于整个开发过程构成一个V字型而得名,详情见图1。尽管很多富有实际经验的测试人员还是不太熟悉V模型,或者其它的模型。V模型已存在了很长时间,和瀑布开发模型有着一些共同的特性,由此也和瀑布模型一样地受到了批评和质疑。V模型中的过程从左到右,描述了基本的开发过程和测试行为。V模型的价值在于它非常明确地标明了测试过程中存在的不同级别,并且清楚地描述了这些测试阶段和开发过程期间各阶段的对应关系。
图1.V模型
从上图可以看出:
强调软件开发的协作和速度,反映测试活动和分析设计关系,将软件实现和验证有机结合起来;
明确界定了测试过程存在不同的级别;
明确了不同的测试阶段和研发过程中的各个阶段的对应关系;
仅仅把测试过程作为在需求分析、系统设计及编码之后的一个阶段,忽视了测试对需求分析;
系统设计的验证,一直到后期的验收测试才被发现;
没有明确的说明早起的测试,不能体现“尽早地、不断地进行软件测试”的原则。
2.W模型
V模型的局限性在于没有明确地说明早期的测试,无法体现“尽早地和不断地进行软件测试”的原则。在V模型中增加软件各开发阶段应同步进行的测试,演化为W模型(如下图)。在模型中不难看出,开发是“V”,测试是与此并行的“V”,详情见图2。基于“尽早地和不断地进行软件测试”的原则,在软件的需求和设计阶段的测试活动应遵循IEEE1012-1998《软件验证与确认(V&V)》的原则。
W模型由Evolutif公司提出,相对于V模型,W模型更科学。W模型是V模型的发展,强调的是测试伴随着整个软件开发周期,而且测试的对象不仅仅是程序,需求、功能和设计同样要测试。测试与开发是同步进行的,从而有利于尽早地发现问题。
W模型也有局限性。W模型和V模型都把软件的开发视为需求、设计、编码等一系列串行的活动,无法支持迭代、自发性以及变更调整。
W模型,也就是双V模型,并不是在V模型上又搞出一个来,而是开发阶段与测试设计阶段同步进行,比如在进行需求分析,软件功能规格说明书评审,软件功能规格说明书基线化后,
系统测试计划,方案,用例也设计完毕,接着是概要设计与集成测试设计,详细设计与
单元测试设计,直到编码完成后,进行代码审查,继续执行单元测试、集成测试、系统测试。
图2.W模型
从上图可以看出:
W模型强调测试伴随着整个软件开发周期,测试与开发并行进行,有利于尽早发现问题;
测试的对象不单单是程序,还有需求和设计等;
W模型有利于即时了解项目的测试风险,及早制定应对方案,加快项目进度;
软件开发和测试保持着线性的前后关系,无法支持迭代、自发性以及需求变更调整等。
3.H模型
在H模型中,软件测试的过程活动完全独立,形成一个独立的流程,贯穿于整个软件的声明周期,与其他流程并发进行,某个测试点准备就绪后就可以从测试准备阶段进行到测试执行阶段,软件测试活动可以根据被测产品的不同而分层进行,详情如图3。
图3.H模型
从上图可以看出:
软件测试不仅仅指测试的执行,还包括很多其他的活动;
软件测试是一个独立的流程,贯穿产品整个生命周期,与其他流程并发地进行;
软件测试要尽早准备,尽早执行;
软件测试是根据被测物的不同而分层次进行的。不同层次的测试活动可以是按照某个次序先后进行的,但也可能是反复的;
把软件的开发视为需求、设计、编码等一系列串行活动,但实际上,这些活动可以交叉的进行,严格的划分只是一种理想状态。
4.X模型
X模型也是对V模型的改进,X模型提出针对单独的程序片段进行相互分离的编码和测试,此后通过频繁的交接,通过集成最终合成为可执行的程序,详情见图4。
图4.X模型
从上图可以看出:
X模型并不要求在进行作为创建可执行程序(图中右上方)的一个组成部分的集成测试之前,对每一个程序片段都进行单元测试(图中左侧的行为);
X模型没能提供是否要跳过单元测试的判断准则;
X模型填补了V模型和X模型的缺陷,并可为测试人员和开发人员带来明显的帮助;
X模型还定位了探索性测试(右下方)。这是不进行事先计划的特殊类型的测试,诸如“我这么测一下结果会怎么样?”,这一方式往往能帮助有经验的测试人员在测试计划之外发现更多的软件错误。
X模型的左边描述的是针对单独程序片段所进行的相互分离的编码和测试,此后将进行频繁的交接,通过集成最终成为可执行的程序,然后再对这些可执行程序进行测试。己通过集成测试的成品可以进行封装并提交给用户,也可以作为更大规模和范围内集成的一部分。多根并行的曲线表示变更可以在各个部分发生。由图中可见,X模型还定位了探索性测试,这是不进行事先计划的特殊类型的测试,这一方式往往能帮助有经验的测试人员在测试计划之外发现更多的软件错误。但这样可能对测试造成人力、物力和财力的浪费,对测试员的熟练程度要求比较高。
作者简介:
王超,花名于龙,2007年计算机软件与理论专业硕士毕业,先后在微软全球技术支持中心、SAP中国研究院、淘宝网从事自动化测试用例开发、自动化框架研发、测试平台建设、信息系统研发、测试工具研发团队管理等工作,目前负责支付宝产品质量部测试工具研发团队。
版权声明:本文出自支付宝产品质量部 王超(花名于龙),51Testing软件测试网原创出品,未经明确的书面许可,任何人或单位不得对本文进行复制、转载或镜像,否则将追究法律责任。
批量执行insert时,有多种执行方式:
1、循环单条执行,类似:
2、合并为一个事务执行(注:Mysql默认事务是自动提交的,需关闭事务自动提交),类似:
事务begin
事务commit
3、采用insert...values value1,value2批量执行,类似:
insert into table_name values (value1),(value2)...(valueN);
下表是在单机情况下,通过
java api调用mysql接口方式执行10,100,500,1000条记录insert时的性能表格
备注:
1 数据表大小未超过innodb_buffer_pool_size。
2 单条记录为308个字节,max_allow_packet设置为16M。
3 innodb_flush_log_at_trx_commit=1,单机无binlog。
4
测试中的执行时间只是一个相对值,不同机器表现肯定不一样。
结论:
1、从实测情况可以知道,在3 的情况下,批量insert性能最高。
2、java api里面的addBatch是1的情形。
造成性能差距的可能原因:
1和2,(1) 1在执行每一条insert时,mysql都会启动一个事务来跟踪;而2减少了mysql的事务数。
(2) 减少了事务日志的同步次数。
1和3,(1) 压缩了网络通信次数,(2) 减少了sql解析次数,(3) 减少了事务数和日志同步次数。
2和3,减少了sql解析次数。
一、线程的创建
头文件
#include <pthread.h>
函数声明
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
在一个线程中调用pthread_create函数创建新的线程后,当前线程从pthread_create处继续往下执行,start_routine为新创建线程的入口函数,start_routine函数接收一个参数,是通过pthread_create的arg参数传递给它的,该参数的类型为void* ,这个指针按什么类型解释由调用者自己定义,start_routine的返回值类型也是void,这个指针的含义同样由调用者自己定义,start_routine返回时,这个线程就退出了,其它线程可以调用pthread_join得到start_routine的返回值。
参数说明
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性,用于指定各种不同的线程属性。
第三个参数是线程运行函数的起始地址,该函数只有一个万能指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。
最后一个参数是运行函数的参数。
返回值
若线程创建成功,则返回0,若线程创建失败,则返回出错编号,并且*thread中的内容是未定义的。
第一个简单的线程程序
#include <stdio.h> #include <pthread.h> void printThread(const char *s) { pid_t pid; pthread_t tid; pid = getpid(); tid = pthread_self(); printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int) pid, (unsigned int) tid, (unsigned int) tid); } void* run(void* arg) { printThread("new thread: "); return NULL; } int main(void) { pthread_t pt; int err = pthread_create(&pt, NULL, run, NULL); if (err != 0) printf("can't create thread: %s\n", strerror(err)); printThread("main thread:"); pthread_join(pt, NULL); return 0; } |
注意
因为pthread并非Linux系统的默认库,而是POSIX线程库。在Linux中将其作为一个库来使用,因此加上 -lpthread(或-pthread)以显式链接该库。
$g++ main.cpp -lpthread -o main
$./main
二、pthread_join函数
函数声明
int pthread_join(pthread_t thread, void **retval);
参数说明
thread: 线程标识符,即线程ID,标识唯一线程。
retval: 用户定义的指针,用来存储被等待线程的返回值。
描述
pthread_join函数,阻塞当前线程的执行,以等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的,新创建的线程默认就是joinable的。
三、线程的两种状态:joinable(默认)和detached
joinable线程可以被其他线程回收,在被其他线程回收之前,它所占用的存储器资源并不被释放,而detached线程不能被其他线程回收,它占用的存储器资源由系统自动释放。
在默认情况下,线程是joinable的,只有当pthread_join函数返回时,创建的线程才算终止,才能释放自己占用的系统资源,而detached线程没有被其他线程等待,线程执行完毕立马释放系统资源。
设置线程状态的函数pthread_attr_setdetachstate,函数声明为pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate);其中第二个参数是可选的PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。
如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如 wait 之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
如果进程中的某个线程执行了pthread_detach(th),则th线程将处于DETACHED状态,这使得th线程在结束运行时自行释放所占用的内存资源,同时也无法由pthread_join()同步,pthread_detach()执行之后,对th请求pthread_join()将返回错误。
四、linux下多线程属性设置
attr的缺省属性值
pthread_attr_t attr;
初始化属性
pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_init 成功完成后将返回0,其他任何返回值都表示出现了错误,并将返回对应的值。 |
销毁属性
使用 pthread_attr_destroy(&attr) 删除初始化期间分配的存储空间,属性对象将会无效,函数成功完成后将返回0,其他任何返回值都表示出现了错误,并将返回对应的值。