The Little Book of Semaphores 信号量小书 第九章 C中的同步

第九章 C中的同步

在本节中,我们将在C中编写一个多线程的同步程序。附录B提供了一些实用程序代码,用于使C代码更加可口。 本节中的示例依赖于该代码。

9.1 互斥

我们首先定义一个包含共享变量的结构体:

counter是一个共享变量,它将由并发线程递增,直到它达到end。 我们将使用数组通过在每次递增后跟踪counter的值来检查同步错误。

9.1.1 父线程代码

以下是父线程用于创建线程并等待它们完成的代码:

第一个循环创建子线程; 第二个循环等待它们完成。 当最后一个子线程完成时,父线程会调用check_array来检查错误。 make_thread和join_thread在附录B中定义。

9.1.2 子线程代码

以下是每个子线程执行的代码:

每次循环时,子线程使用计数器counter作为数组array的索引并递增相应的元素。 然后他们增加计数器counter并检查它们是否完成。

9.1.3 同步错误

如果一切正常,数组的每个元素都应该递增一次。 因此,为了检查错误,我们可以计算不是1的元素数量:

您可以从greenteapress.com/semaphores/counter.c下载该程序(包括清理代码)

如果您编译并运行该程序,您应该看到如下输出:

当然,子线程之间的交互取决于操作系统的详细信息,以及计算机上运行的其他程序。在本文所示的示例中,一个线程在另一个线程启动之前从0一直运行到结束,所以没有同步错误也就不足为奇了。

但随着end变得越来越大,子线程之间的上下文切换也越来越多。 在我的系统中,当end为100,000,000时,我开始看到错误。

思考:使用信号量强制对共享变量进行独占访问,然后再次运行程序以确认没有错误。

9.1.4 互斥提示

以下是我的解决方案中使用的Shared版本

第5行将mutex声明为信号量; 第20行使用数值1来初始化mutex。

9.1.5 互斥方案

以下是子线程代码的同步版本:

这里没有什么太令人惊讶的了; 唯一棘手的事情是记住在return语句之前释放互斥锁。

您可以从greenteapress.com/semaphores/counter_mutex.c下载此解决方案。

9.2 制作自己的信号量

对于使用Pthreads的程序,最常用的同步工具是互斥锁和条件变量,而不是信号量。有关这些工具的解释,我推荐Butenhof的《POSIX Threads编程》[2]。

思考:阅读有关互斥锁和条件变量的内容,然后使用它们编写信号量的实现。

您可能希望在解决方案中使用以下实用程序代码。 这是我封装的Pthreads互斥量:

以及我封装的Pthread条件变量:

9.2.1 信号量实现提示

这是我用于信号量的结构体定义:

value是信号量的值。 wakeups计算未决信号的数量; 也就是说,已被唤醒但尚未恢复执行的线程数。 唤醒的原因是为了确保我们的信号量具有第4.3节中描述的属性3。

mutex提供对value和wakeups的独占访问; cond是线程等待的条件变量,如果他们等待信号量的话。

以下是此结构的初始化代码:

9.2.2 信号量实现

这是我使用Pthread的互斥量和条件变量实现的信号量:

大部分都是直截了当的; 唯一可能棘手的是在第7行的do ... while循环。这是使用条件变量的一种不寻常的方式,但在这种情况下它是必要的。

思考:为什么我们不能用while循环替换这个do... while循环?

9.2.3 信号量实现细节

使用while循环,此实现将不具有属性3。一个线程可能会发出信号,然后运行回来并捕获它自己的信号。

使用do ... while循环,可以保证,当一个线程发出信号时,另一个等待的线程将得到信号,即使另一个线程在其中一个等待线程恢复之前获得第3行的互斥锁。

【好吧,差不多。 事实证明,适时的虚假唤醒(参见http://en.wikipedia.org/wiki/Spurious_wakeup)可能会违反此保证。】

猜你喜欢

转载自blog.csdn.net/booksyhay/article/details/83011808
今日推荐