并行计算导论
顺序算法与并行算法
- 顺序算法:
begin-end
代码块,所有步骤都是通过单个任务依次执行的,每次执行一个步骤。当所有步骤执行完成时,算法完成。 - 并行算法:
cobegin-coend
代码块,所有任务都是并行执行的。下一个步骤将只在所有这些任务完成之后执行。
并行性与并发性
- 并行性:只能在有多个处理组件的系统中实现。
- 并发性:不同的任务只能并发执行,即在逻辑上并行执行。
线程
优点
- 线程创建和切换速度更快
- 线程的响应速度更快
- 线程更适合并行计算
缺点
- 需要来自用户的明确同步
- 许多库函数可能对线程不安全
- 在单CPU系统上,使用线程解决问题实际上要比使用顺序程序慢
线程操作
线程可在内核模式或用户模式下执行
线程管理函数
创建线程
pthread_create()
函数:成功返回0,如果失败则返回错误代码
线程ID
pthread_equal()
函数:比较线程ID,不同返回0
线程终止
线程函数结束后终止,或使用函数pthread_exit
进行显示终止
0表示正常终止
线程连接
pthread_join
:终止线程的退出状态一status_ptr
返回
线程同步
当多个线程试图修改同一共享变量或数据结构时,如果修改结果取决于线程的执行顺序,则称之为竞态条件
互斥量
初始化
-
静态方法,如
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
定义互斥量m
-
动态方法:
pthread_mutex_init()
函数
死锁预防
互斥量使用封锁协议
条件变量
作为锁,互斥量仅用于确保县城只能互斥地访问临界区中的共享数据对象
条件变量提供了一种线程协作的方法,初始化:
-
静态方法,如:
pthread_cond_t con = PTHREAD_COND_INITIALIZER;
定义一个条件变量con
-
动态方法:
pthread_cond_init()
函数
生产者—消费者问题
信号量
信号量是进程同步的一般机制。信号量是一种数据结构
屏障
在某些情况下,保持线程活动会更好,但应要求他们在所有线程都达到指定同步点之前不能继续活动。
在Pthreads
中,可以采用的机制是屏障以及一系列屏障函数。首先,主线程创建一个屏障对象:
pthread_barrier_t barrier;
并且调用
pthread_barrier_init(&barrier NULL, nthreads);
初始化
工作线程使用
pthread_barrier_wait(&barrier)
在屏障中等待指定数量的线程到达屏障
linux中的线程
linux不区分进程和线程
线程只是一个与其他进程共享某些资源的进程
进程和线程都是由clone()
系统调用创建的,具有以下原型:
int clone(int (*fn)(coid *), void *child_stack, int flags, void *arg)
clone()
更像是一个线程创建函数,flag字段详细说明父进程和子进程共享的资源,包括:
CLONE_VM
:父进程和子进程共享地址空间CLONE_FS
:父进程和子进程共享文件系统信息CLONE_FILES
:父进程和子进程共享打开的文件CLONE_SIGHAND
:父进程和子进程共享信号处理函数和已屏蔽信号
问题和解决思路
pthread_cancel()不能杀死线程
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <pthread.h>
5 #include <string.h>
6
7 void *func(void *arg){
8 while(1);
9 return NULL;
10 }
11 int main(){
12 printf("main:pid=%d,tid=%lu\n",getpid(),pthread_self());
13
14 pthread_t tid;
15 int ret = pthread_create(&tid,NULL,func,NULL);
16 if(ret != 0){
17 fprintf(stderr,"pthread_create error:%s\n",strerror(ret));
18 return 1;
19 }
20
21 ret = pthread_cancel(tid);
22 if(ret != 0){
23 fprintf(stderr,"pthread_cancel error:%s\n",strerror(ret));
24 return 2;
25 }
26
27 pthread_exit((void*)0);
28
29 return 0;
30 }
原因:
使用pthread_cancel()终止线程,需要线程中存在取消点。
大致是需要线程中有陷入内核的操作,才会存在取消点
如果想要用pthread_cancel()终止一个没有陷入内核操作的线程,就需要手动添加取消点
在while循环中加入,pthread_testcancel()即可用pthread_cancel()杀死该线程
7 void *func(void *arg){
8 while(1) pthread_testcancel();
9 return NULL;
10 }