用互斥锁来管理交通

为了保护某段代码的安全,你需要创建互斥锁:

  1. pthread_mutex_t a_lock = PTHREAD_MUTEX_INITIALIZER;

互斥锁必须对所有可能发生冲突的线程可见,也就是说它是一个全局变量。

PTHREAD_MUTEX_INITIALIZER实际上是一个宏,当编译器看到它,就会插入创建互斥锁的代码。

  • 红灯停。

你需要把第一盏红绿灯放在这段代码的开头,pthread_mutex_lock()只允许一个线程通过,其他线程运行到这行代码时必须等待。

用互斥锁来管理交通 - 图1

  • 绿灯行。

当线程到达代码的尾部就会调用pthread_mutex_unlock()把红绿灯调回绿灯,其他线程就能进入这段代码了:

用互斥锁来管理交通 - 图2

既然你知道了怎么在代码中创建锁,也就能精确控制线程的行为了。

用互斥锁来管理交通 - 图3把long值传给线程函数

线程函数可以接收一个void指针作为参数,并返回一个void指针值。通常你希望把某个整型值传给线程,并让它返回某个整型值,一种方法是用long,因为它的大小和void指针相同,可以把它保存在void指针变量中。

用互斥锁来管理交通 - 图4

 

用互斥锁来管理交通 - 图5练习

找到上锁的位置实非易事,而锁的位置会改变代码的运行方式。下面有两个不同版本的drink_lots()函数,它们以不同方式为代码上了锁。

用互斥锁来管理交通 - 图6

两段代码都用互斥锁来保护beers变量的安全,并在退出前显示了beers值。由于它们在不同的位置使用了锁,因此在屏幕上输出了不同结果。

你能弄清哪段代码对应哪个版本吗?

用互斥锁来管理交通 - 图7

 

用互斥锁来管理交通 - 图8练习解答

找到上锁的位置实非易事,而锁的位置会改变代码的运行方式。下面有两个不同版本的drink_lots()函数,它们以不同方式为代码上了锁。

两段代码都用互斥锁来保护beers变量的安全,并在退出前显示了beers值。由于它们在不同的位置使用了锁,因此在屏幕上输出了不同结果。

请弄清哪段代码对应哪个版本。

用互斥锁来管理交通 - 图9

用互斥锁来管理交通 - 图10恭喜!你已经(快要)看完这本书了。打开一瓶啤酒,庆祝一下吧!

是时候决定你将成为哪种类型的C程序员了。写纯C代码的Linux黑客?还是为Arduino那种小装置写嵌入式C的匠人?或是转而成为一名使用C++的游戏开发人员?或使用Objective-C的Mac及iOS程序员?

无论你的选择是什么,你都已经成为了C社区的一份子。在这里,你们使用同一种语言,并深深热爱着它。这种语言创建的软件比其他任何语言都要多;它是整个互联网和几乎所有操作系统的基础;几乎所有其他语言都是用它写的;几乎所有电子设备的处理器都可以用它来编程,大到飞机卫星,小到手表手机。

欢迎你!一年级C黑客!

这里没有蠢问题

**问:为了支持多线程,我的计算机必须有多个处理器吗?

:不必。绝大多数计算机都使用多核处理器。也就是说CPU中有一些小型处理器,它们可以一次做几件事情。即便代码运行在一台单核/单处理器的计算机上,也还是能运行多线程程序。

问:怎么运行?

:操作系统会在多个线程之间快速地切换,看起来就好像在同时做多件事。

问:线程能让程序变得更快吗?

:也不一定,尽管线程可以帮助你利用机器上更多的处理器和核,但你还是需要控制代码中锁的数量,如果用了太多锁,代码可能会像单线程程序一样慢。

问:怎样设计高效的多线程程序?

:减少线程需要访问的共享数据的数量。如果线程无需访问很多共享数据,那么多个线程等一个线程的情况就很少出现,速度会大大提高。

问:线程要比进程快?

:通常是这样,因为创建进程要比创建线程花更多时间。

问:听说互斥锁会引发“死锁”,那是什么玩意儿?

:假设你有两个线程,它们都想得到互斥锁A和B。倘若第一个线程得到了A,第二个线程得到了B,这两个线程就会陷入死锁。因为第一个线程无法得到B,第二个线程无法得到A,它俩都停滞不前。