第四章学习笔记
第四章 并发编程
摘要
- 介绍了并行计算的概念,指出了并行计算的重要性;
- 比较了顺序算法与并行算法,以及并行性与并发性;
- 解释了线程的原理及其相对于进程的优势;
- 通过示例介绍了 Pthread 中的线程操作,包括线程管理函数,互斥量、连接、条件变量和屏障等线程同步工具;
- 通过具体示例演示了如何使用线程进行并发编程,包括矩阵计算、快速排序和用并发线程求解线性方程组等方法;
- 解释了死锁问题,并说明了如何防止并发程序中的死锁问题;
- 讨论了信号量,并论证了它们相对于条件变量的优点。
一、知识点归纳以及收获内容
并行计算导论
- 顺序算法与并行算法

- 并行性与并发性 通常,并行算法只识别可并行执行的任务,但是它没有规定如何将任务映射到处理组件。在理想情况下,并行算法中的所有任务都应该同时实时执行。然而,真正的并行执行只能在有多个处理组件的系统中实现,比如多处理器或多核系统。在单 CPU 系统中,一次只能执行一个任务。在这种情况下,不同的任务只能并发执行、即在逻辑上并行执行。在单CPU系统中,并发性是通过多任务处理来实现的,该内容已在第3章中讨论过。在本章的最后,我们将在一个编程项目中再次讲解和示范多任务处理的原理和方法。
线程
- 线程的原理
线程是某进程同一地址空间上的独立执行单元。创建某个进程就是在一个唯一地址空间创建一个主线程。当某进程开始时,就会执行该进程的主线程。如果只有一个主线程,那么进程和线程实际上并没有区别。但是,主线程可能会创建其他线程。每个线程又可以创建更多的线程等。
- 线程的优点
- 线程创建和切换速度更快;
- 线程的相应速度更快;
- 线程更适合并行计算;
- 线程的缺点
(1)由于地址空间共享,线程需要来自用户的明确同步。
(2)许多库函数可能对线程不安全,例如传统 strtok()函数将一个字符串分成一连串令牌。通常,任何使用全局变量或依赖于静态内存内容的函数,线程都不安全。为了使库函数适应线程环境,还需要做大量的工作。
(3)在单 CPU系统上,使用线程解决问题实际上要比使用顺序程序慢,这是由在运行时创建线程和切换上下文的系统开销造成的。
线程管理函数
(1)创建线程
使用pthread_create()函数创建线程。
int prhread_create (pthread_t *pthread_id,pthread_attr_t *attr, Void *(*func)(void *), void *arg);
如果成功则返回0,如果失败则返回错误代码。
其中,attr参数最复杂。下面给出了 attr参数的使用步骤。
1.定义一个pthread属性变量 pthread_attr_t attr。
2.用pthread_attr_init(&attr)初始化属性变量。
3.设置属性变量并在 pthread_create()调用中使用。
4.必要时,通过 pthread_attr_destroy(&attr)释放 attr资源。
(2)线程ID
线程 ID是一种不透明的数据类型,取决于实现情况。因此,不应该直接比较线程 ID。如果需要,可以使用 pthread_equal()函数对它们进行比较。
int pthread_equal (pthread_t t1, pthread_t t2);
如果是不同的线程,则返回0,否则返回非0。
(3)线程终止
线程函数结束后,线程即终止。或者,线程可以调用函数
int pthread_exit (void *status);
进行显示终止,其中状态是线程的退出状态。通常,0退出值表示正常终止,非0只表示异常终止。
(4)线程连接
一个线程可以等待另一个线程的终止,通过:
int pthread_join (pthread_t thread,void **status_ptr);
终止线程退出状态以status_ptr返回。
线程同步
-
当多个线程试图修改同一共享变量或数据结构时,如果修改结果取决于线程的执行顺序,则称之为竞态条件。
-
互斥量
- 在 Pthread中,锁被称为互斥量,意思是相互排斥。
- 互斥变呈是用
ptbread_mutex_t类型声明的在使,用之前必须对它们进行初始化。- 有两种方法可以初始化互斥址:
- 静态方法:
pthreaa—mutex_t m = PTHREAD_MUTEX_INITIALIZER,定义互斥量 m, 并使用默认属性对其进行初始化。 - 动态方法,使用 pthread_ mutex _init() 函数
- 静态方法:
- 有两种方法可以初始化互斥址:
- 线程通过互斥量来保护共享数据对象
-
死锁预防
- 死锁是一种状态,在这种状态下,许多执行实体相互等待,因此都无法继续下去。
- 死锁预防,试图在设计并行算法时防止死锁的发生。
- 一种简单的死锁预防方法是对互斥量进行排序,并确保每个线程只在一个方向请求互斥量,这样请求序列中就不会有循环。
-
条件变量
- 条件变量提供了一种线程协作的方法。
- 在Pthread中,使用类型
pthread_cond_t来声明条件变量,而且必须在使用前进行初始化。 - 与互斥变量一样,条件变量也可以通过两种方法进行初始化。
- 静态方法:
pthread_cond_t con= PTHREAD_COND_INITIALIZER;定义一个条件变屾con,并使用默认属性对其进行初始化。 - 动态方法:使用
pthread_cond_init()函数,可通过attr参数设置条件变量。为简便起见,我们总是使用NULLattr参数作为默认属性。
- 静态方法:
-
信号量
- 信号量是进程同步的一般机制。
- 信号量是数据结构
struct sem{ int value; struct process *queue }s;
- 屏障
线程连接操作允许某线程(通常是主线程)等待其他线程终止。在等待的所有线程都终止后,主线程可创建新线程来继续执行并行程序的其余部分。创建新线程需要系统开销。在某些情况下,保持线程活动会更好,但应要求它们在所有线程都达到指定同步点之前不能继续活动。
二、问题及解决
进程与线程区别?
总的来说线程就可以当做是进程里面的执行的单元,同时它也是这个进程里面的一个能够调度的实体。首先来说进程和线程两个全是基本单元,完全是为了操作系统运行程序而存在的。该类系统为了要实现应用程序的并发性处理,就必须要运用该基本单元。因此它们之间有相似处也有区别。线程和进程的区别可以表现为以下的几个因素:
- 线程和进程的区别一
简单地讲,任何的一个程序都必须有且有一个以上的进程,而相对于一个进程而言也必须要有且有一个以上的线程。相对于进程而言,对线程进行划分的尺度一般要小很多,这就导致了多线程的一些程序能够出现更高的并发性。
- 线程和进程的区别二
在执行进程的时候,一般会具有相互独立的多个内存单元。但是多个线程是可以共享内存的,这样运行效率就很大的程度上被提高了。相对于单个的独立线程而言都会有相应程序的运行入口以及一些程序等出口。线程就不一样了,它不能独立的去执行而必须要依附在相应的应用程序里面。这样的话应用程序就可以执行多个线程并进行相应的控制。
- 线程和进程的区别三
通过了解逻辑角度我们可以得知,多线程这样的意义是相对于在一个应用程序里面的,能够同时的执行。而操作系统不会认为多个线程就是多个独立应用,因此也就不会使其调度以及管理实现资源的分配。
简单地讲线程就是运行活动的集合,它是所有独立功能程序集中于一点的数据集合,进程是独立的单位,它是由系统来进行分配资源以及调度的。
换句话说线程可以是进程的实体,也就是CPU调度以及分派的一个很小的体系,可以说它要比进程小很多的基本单位。线程不具备任何的系统资源,它在同样一个进程里面与其他线程共享全部资源。其中一个线程既能够创建也可以撤销其他的线程。同样的,它们之间也能够并发的执行。
浙公网安备 33010602011771号