一、知识点归纳
(一)知识点内容
教材学习内容总结
本章论述了并发编程,介绍了并行计算的概念,指出了并行计算的重要性;比较了顺序算法与并行算法, 以及并行性与并发性;解释了线程的原理及其相对于进程的优势;解释了死锁问题, 并说明了如何防止并发程序中的死锁问题;讨论了信号量, 并论证了它们相对千条件变量的优点;还解释了支待 Linux 中线程的独特方式。
4.1 并行计算
1. 背景:
早期计算机只有一个处理组件,称为处理器或中央处理器,程序多进行串行计算,而基于分治原则的算法具有高度并行性,因此出现尝试使用多个执行并行算的处理器来解决问题的并行计算方案。
2. 顺序算法与并行算法
-
顺序算法所有步骤都是通过单个步骤依次执行的,所有步骤执行完成则算法结束。
-
并行算法中同一个代码块中的步骤并行执行,在全部完成后再执行下一个代码块的步骤。
4.2 线程
1. 线程原理
一个操作系统包含许多并发进程。在进程模型中,进程是独立的执行单元。所有进程均是在内核模式和用户模式下进行的。
-
内核模式下:各进程在唯一地址空间上执行,与其他进程是分开的,多个独立的进程共用同一个执行路径,当某进程必须等待某事件时,整个进程就会停止执行。
-
线程是某进程同一地址空间上的独立执行单元。创建某个进程就是在一个唯一地址空间创建一个主线程。
2. 线程优点
- 线程创建和切换速度更快
- 线程的响应速度更快
- 线程更适合并行计算
3. 线程缺点
- 由于地址空间共享,线程需要来自用户的明确同步
- 许多库函数可能对线程不安全
- 在单CPU系统上,使用线程解决问题实际上要比使用顺序程序更慢。
4.3 线程操作
线程的执行轨迹与进程类似。用户可在内核模式或用户模式下进行。
- 用户模式下,线程在进程的相同地址空间中执行,但每个线程都有自己的执行堆栈。线程是独立的执行单元,可根据操作系统内核调度策略,对内核进行系统调用,变为挂起、激活已继续执行。
4.4 线程管理函数
1、 创建线程 使用pthread_create()
函数创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
2、 线程ID 使用pthread_equal()函数对线程ID进行比较
int pthread_equal(pthread_t t1,pthread_t t2);
不同线程返回0,否则返回非0 `
3、 线程终止 线程可以调用函数进行终止
void pthread_exit(void *status);
0退出值表示正常终止,非0值表示异常终止
4、 线程连接 一个线程可以等待另一个线程的终止,通过函数终止线程的退出状态。
int pthread_join (pthread_t thread, void **status ptr);
(二)苏格拉底挑战
1、问题一
2、问题二
二、问题与解决思路
(一)问题
在给矩阵排序时,编译出现错误
(二)解决思路
经询问GPT和同学,我得知需要先加-pthread
,随后编译成功
三、实践过程
(一)矩阵排序
(二) 计算矩阵和
1、代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define N 4
int A[N][N], sum[N];
void *func(void *arg)
{
int j, row;
pthread_t tid = pthread_self(); // get thread ID number
row = (int)arg; // get row number from arg
printf("Thread %d [%lu] computes sum of row %d\n", row, tid, row);
for (j=0; j<N; j++)
{
sum[row] += A[row][j];
}
printf("Thread %d [%lu] done sum[%d] = %d\n",row, tid, row, sum[row]);
pthread_exit((void*)0); // thread exit: 0=normal termination
}
int main (int argc, char *argv[])
{
pthread_t thread[N]; // thread IDs
int i, j, r, total = 0;
void *status;
printf("Main: initialize A matrix\n");
for (i=0; i<N; i++)
{
sum[i] = 0;
for (j=0; j<N; j++)
{
A[i][j] = i*N + j + 1;
printf("%4d" ,A[i][j]);
}
printf("\n");
}
printf("Main: create %d threads\n", N);
for(i=0; i<N; i++)
{
pthread_create(&thread[i], NULL, func, (void *)i);
}
printf("Main: try to join with threads\n");
for(i=0; i<N; i++)
{
pthread_join(thread[i], &status);
printf("Main: joined with %d [%lu]: status=%d\n",i, thread[i], (int)status);
}
printf("Main: compute and print total sum:");
for (i=0; i<N; i++)
{
total += sum[i];
}
printf("tatal = %d\n", total);
pthread_exit(NULL);
}
2、具体步骤