20201220蔡笃俊《信息安全系统设计与实现》第四章学习笔记

一、任务内容

  • 自学教材第4章,提交学习笔记(10分)
  • 知识点归纳以及自己最有收获的内容 (3分)
  • 问题与解决思路(2分)
  • 实践内容与截图,代码链接(3分)
  • ...(知识的结构化,知识的完整性等,提交markdown文档,使用openeuler系统等)(2分)

二、知识点归纳以及自己最有收获的内容

(一)知识点归纳

本章论述了并发编程,介绍了并行计算的概念。指出了并行计算的重要性;比较了顺序算法与并行算法,以及并行性与并发性;解释了线程的原理及其相对于进程的优势;介绍了Pthread中的线程操作,包括线程管理函数,互斥量、连接、条件变量和屏障等线程同步工具;解释了死锁问题,并说明了如何防止并发程序中的死锁问题;讨论了信号量,并论证了它们相对于条件变量的优点;还解释了支持Linux中线程的独特方式。

1. 并行计算导论

begin-end代码块中的顺序算法可能包含多个步骤。所有步骤都是通过单个任务依次执行的,每次执行一个步骤。当所有步骤执行完成时,算法结束。相反,右侧为并行算法的描述,它使用cobegin-coend代码块来指定并行算法的独立任务。在cobegin-coend块中.所有任务都是并行执行的,紧接着cobegin-coend代码块的下一个步骤将只在所有这些任务完成之后执行。

---       顺序算法      --------      并行算法       ---
       begin              |         cobegin
         step_1           |           task_1
         step_2           |           task_2
         ...              |           ...
         step_n           |           task_n
       end                |         coend
       //next step        |         //next step
-----------------------------------------------------

通常,并行算法只识别可并行执行的任务,但是它没有规定如何将任务映射到处理组件。在理想情况下,并行算法中的所有任务都应该同时实时执行。然而,真正的并行执行只能在有多个处理组件的系统中实现,比如多处理器或多核系统。在单 CPU 系统中—次只能执行一个任务。在这种情况下,不同的任务只能并发执行,即在逻辑上并行执行。在单CPU 系统中,并发性是通过多任务处理来实现的。

2. 线程

*线程原理:

线程是某进程同一地址空间上的独立执行单元。创建某个进程就是在一个唯一地址空间创建一个线程。当某进程开始时,就会执行该进程的主线程。如果只有一个主线程,那么进程和线程实 际上并没有区别。但是,主线程可能会创建其他线程。每个线程又可以创建更多的线程等。某进程的所有线程都在该进程的相同地址空间中执行,但每个线程都是一个独立的执行单元。

*线程优缺:

  • 优点:线程创建和切换速度更快。若要在某个进程中创建线程,操作系统不必为新的线程分配内存和创建页表,因为线程与进程共用同一个地址空间。所以,创建线程比创建进程更快。线程的响应速度更快。一个进程只有一个执行路径。当某个进程被挂起时,帮个进程都将停止执行。相反,当某个线程被挂起时,同一进程中的其他线程可以继续执行。线程更适合井行计算。并行计算的目标是使用多个执行路径更快地解决间题。基于分治原则(如二叉树查找和快速排序等)的算法经常表现出高度的并行性,可通过使用并行或并发执行来提高计算速度。
  • 缺点:由于地址空间共享,线程需要来自用户的明确同步,并且许多库函数可能对线程不安全。而且在单CPU系统上,使用线程解决间题实际上要比使用顺序程序慢,这是由在运行时创建线程和切换上下文的系统开销造成的。

3. 线程管理函数

1. 创建线程

使用pthread_create()函数创建线程:
int pthread_create (pthread_t *pthread_id, pthread_attr_t *attr, void *(*func)(void *), void *arg);

2. 线程ID

线程ID是一种不透明的数据类型,取决于实现情况。因此,不应该直接比较线程ID。 如果需要,可以使用pthread_equal()函数对它们进行比较:int pthread_equal (pthread_t tl, pthread_t t2);

3. 线程终止

线程函数结束后,线程即终止。或者,线程可以调用函数int pthread_exit (void *status);进行显式终止,其中状态是线程的退出状态。通常,0退出值表示正常终止,非0值表示异常终止。

4. 线程连接

一个线程可以等待另一个线程的终止,通过int pthread_join (pthread_t thread, void **status ptr);终止线程的退出状态以status_ptr返回。

4. 线程同步

1. 互斥量

  • 我们可以通过静态方法定义互斥量m,并使用默认属性对其进行初始化。

    • pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER
  • 我们也可以通过动态方法:使用pthread_mutex_init()函数,可通过attr参数设置互斥属性。

    • pthread_mutex_init(pthread_mutex_t *m,pthread_mutexattr_t,*attr);

2. 死锁预防

有多种方法可以解决可能的死锁问题,其中包括死锁预防、死锁规避、死锁检测和恢复等。在实际系统中,唯一可行的方法是死锁预防,试图在设计并行算法时防止死锁的发生。一种简单的死锁预防方法是对互斥量进行排序,并确保每个线程只在一个方向请求互斥量,这样请求序列中就不会有循环。

但是,仅使用单向加锁请求来设计每个并行算法是不可能的。在这种情况下,可以使用条件加锁函数 pthread mutex trylock(来预防死锁。如果互斥量已被加锁,则 trylock(函数会立即返回一个错误。在这种情况下,调用线程可能会释放它已经获取的一些互斥量以便进行退避,从而让其他线程继续执行。我们可以重新设计其中一个线程,利用条件加锁和退避来预防死锁。

3. 信号量

信号量和条件变量之间的主要区别是,前者包含一个计数器,可操作计数器,测试计数器值以做出决策等,所有这些都是临界区的原子操作或基本操作,而后者需要一个特定的互斥量来执行临界区。在 Pthreads 中,互斥量严格用于封锁。而条件变量可用于线程协作。相反,可以把使用初始值1计算信号量当作锁。带有其他初始值的信号量可用于协作。因此,信号量比条件变量更通用、更灵活。

三、实践过程中产生的问题与解决思路

我在尝试用线程快速排序时,遇到了始终不能编译成功的问题,一直出现“undefined reference to ‘pthread_create’”错误,导致无法编译通过。

后来经查询,找到了问题原因:因为pthread不是Linux下的默认的库,也就是在链接的时候,无法找到phread库中函数的入口地址,于是链接会失败。

解决办法:我们在gcc编译的时候,在可执行文件后附加上-lpthread参数即可解决。

posted @ 2022-10-16 09:13  acacacac  阅读(45)  评论(0编辑  收藏  举报