📄 15.4.2 多线程程序容易出现的问题.txt
字号:
15.4.2 多线程程序容易出现的问题
事实上,上述 MultiThread程序存在一个隐患,它有一个潜在的问题,例如,当变量 tickets为 1
时,线程 1函数 Funl Proc进入 if语句块后(即上述例 15-2所示该函数的第 10
. 行代码处),正好该线程的时间片到期了,操作系统就会选择线程 2让其执行,而这时变量
tickets的值还没有减1,因此这时变量 tickets的值仍是1.线程 2进入它的证语句块中,
于是线程2执行卖票操作,打印出票号1,然后tickets变量执行自减操作,其值变为0。如果当线程2
执行完成上述操作之后,正好又轮到线程1开始运行了。而这时线程1上次是执行到 if语句块之后才
暂停的,因此它将从这个地方(即上述例 15-2所示代码中 FunlProc函数的第 10行代码处〉继续开
始执行,于是它输出当前的票号,而这时tickets变量的值已经是0了,也就是说,我们会看到线程
1~卖了号码为0的票。当然这种情况是不允许的。有些读者会认为这种情况可能太凑巧了吧,刚才运
行时也没有看到这种情况发生。但是读者应注意,火车站售票系统是一个长时间运行的系统,在长
时间的运行过程中,两个线程会发生频繁的切换,在这种切换过程中,就有可能会出现上述所说的
这种情况,一旦出现这种情况,最后的结果是不可预料的。对于火车站来说,如果一个座位卖出两
张票,那么这个后果是比较严重的。自然,火车站就会找到原先开发这套系统的公司让其进行修改,
而最终的修改任务一定是落到我们程序员的头上。对程序员来说,在修改一个软件bug时,往往需要
重现该错误,然后才能查找该错误的原因。于是,程序员就会开始运行这套系统,希望能够捕捉到
这个错误,然而这种错误只是在一些特殊的情况下才会发生,程序员可能运行了好几天也没有看到
该错误重现。对于一个不可重现的错误来说,修改起来是非常困难的。因此,这就要求我们程序员
在编写代码时,应尽量避免这种错误的发生。
上述问题的出现主要是因为两个线程访问了同一个全局变量: tickets。为了避免这种问题的发生,
就要求在多个线程之间进行一个同步处理,保证一个线程访问共享资源时,其他线程不能访问该资
源。对本例来说,就是当一个线程在销售火车票的进程中,其他线程在该时间段内不能访问同一种
资源,本例就是指全局变量: tickets,必须等到前者完成火车票的销售过程之后,其他线程才能访
问该资源。这与我们在商场买衣服时所进行的活动类似,当我们在试衣间进行试衣服这一活动时,
其他试衣服的人必须等待,只有等待我们完成了试衣服这一活动井离开试衣间时,其他人才能进入
该试衣间。
为了能够看到上面所说的那种情况,在上述例 15-2所示线程函数中,当进入江语句中之后,立即
调用Sleep函数,让线程睡眠片刻。即在上述例 15-2所示代码中两个线程入口函数的if语句块中添
加下面这行语句,并使其成为该语句块的第一行代码:
Sleep(1);
现在,我们来分析一下MultiThread程序的执行过程,当程序进入到线程1的if语句块时,就会调用
新添加的Sleep语旬,从而线程1就要放弃其执行的权利,操作系统就会选择另一个线程来执行。于
是线程2开始执行,进入到它的if语句中,就会调用它的Sleep函数,因此线程2也睡眠了。于是又轮
到线程I继续执行,打印出当前销售的票号,票号减 1。对线程2来说,当其暂停时间到了之后,又
继续从其Sleep函数后面的语句开始执行,也打印出当前销售的票号,票号减 1。然后线程1和线程2
继续按照这种方式执行下去,直到卖完所有的 1∞张票。
我们可以再次运行 MultiThread程序,看看会出现什么样的情况。这时,程序界面如图 15.10所示。
从运行结果可以看到程序最后会销售出0号的票。这就是上面所说的多个线程访问同一个资源时可能
会出现的情况。当然,这里我们是故意为地让线程发生切换。但是如果系统
采用如例 15-2所示代码实现的话,当系统长时间运行时,也可能会出现这样的情况。一般
来说,对多线程程序,如果这些线程需要访问共享资源,就需要进行线程间的同步处理。
图 15.10出现票号为 0的结果
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -