12.8 多线程

至此,我们总是让程序的主执行线程仅仅创建一个线程。但我们并不想让读者认为你只能多创建一个线程。

实 验 多线程

在本章最后的例子thread8.c中,我们将演示如何在同一个程序中创建多个线程,然后又如何以不同于其启动的顺序将它们合并到一起。

12.8 多线程 - 图1

12.8 多线程 - 图2

运行这个程序时,将看到如下所示的输出结果:

12.8 多线程 - 图3

如你所见,我们创建了许多线程并让它们以随意的顺序结束执行。这个程序有一个小漏洞,如果将sleep调用从启动线程的循环中删除,它就会变得很明显。我们通过它提醒读者,在编写使用线程的程序时需要多么小心。你发现错误在哪里了吗?我们将在下面的“实验解析”中解释它。

实验解析

这一次,我们创建了一个线程ID的数组,如下所示:

12.8 多线程 - 图4

然后通过循环创建多个线程,如下所示:

12.8 多线程 - 图5

创建出的线程等待一段随机的时间后退出运行,如下所示:

12.8 多线程 - 图6

在主(原先)线程中,我们等待合并这些子线程,但并不是以创建它们的顺序来合并,如下所示:

12.8 多线程 - 图7

如果删除sleep调用后再运行这个程序,就可能会看到一些奇怪的现象,比如一些线程以相同的参数被启动,你可能会看到类似下面的输出:

12.8 多线程 - 图8

12.8 多线程 - 图9

你能发现为什么会出现这样的问题吗?启动线程时,线程函数的参数是一个局部变量,这个变量在循环中被更新,引起问题的代码行是:

12.8 多线程 - 图10

如果主线程运行得足够快,就有可能改变某些线程的参数(即lots_of_threads)。当对共享变量和多个执行路径没有做到足够重视时,程序就有可能出现这样的错误行为。我们已经警告过,编写线程程序时需要在设计上特别小心。要改正这个问题,我们可以直接传递这个参数的值,如下所示:

12.8 多线程 - 图11

当然还要修改thread_function函数,如下所示:

12.8 多线程 - 图12

这些修改都在程序thread8a.c中以阴影部分显示出来了,如下所示:

12.8 多线程 - 图13

12.8 多线程 - 图14

12.8 多线程 - 图15

12.8 多线程 - 图16

12.8 多线程 - 图17

12.8 多线程 - 图18