作者|Ricky的水果摊
时间|2022年6月30日
文章目录
实验目的
加深对进程概念的理解,认识并发执行的实质,分析进程征用资源的现象, 学习进程、线程互斥的方法。
实验原理
1.线程的基本概念
-
线程是一条执行路径,是计算机中 独立运行的最小单位,它是进程的一个执行流,是 CPU调度和分派的基本单位
-
一个进程可以由多个线程组成,线程间 共享进程的所有资源,但每个线程又有各自的堆栈和局部变量
- 对于单核CPU而言:多线程就是一个CPU在来回的切换,在交替执行。
- 对于多核CPU而言:多线程就是同时有多条执行路径在同时(并行)执行,每个核执行一个线程,多个核就有可能是一块同时执行的。
-
一个正在运行的软件(如百度网盘)就是一个进程,一个进程可以同时运行多个任务( 百度网盘可以同时下载多个文件,每个下载任务就是一个线程),可以简单的认为进程是线程的集合。
2.进程与线程的关系
一个操作系统中可以有多个进程,一个进程中可以包含一个线程(单线程程序),也可以包含多个线程(多线程程序)
线程相对于进程的优点
- 在一个程序中需要并发处理多个任务时(例如需要并发监控2个不同的fifo),以多线程的方式编程更符合人的思维
- 多线程编程使得多个任务间的 通信更加直观和方便(例如可以使用共享的全局变量)
- 多线程的 切换开销 更小
3.线程的相关函数
3.1 pthread_self()
pthread_self()功能
获取当前线程的ID,类似于用
getpid()
获取当前进程PID
pthread_self()原型
#include <pthread.h>
pthread_t pthread_self(void);
pthread_self()返回值
返回当前线程的ID
pthread_self()参数解释
该函数无传入参数
pthread_self()编程实例
pthread_t tid //可以理解为 typedef unsigned long int pthread_t
tid = pthread_self(); //获取当前线程ID
printf("Current thread's ID is %lu\n", tid);
3.2 pthread_create()
pthread_create()功能
创建一个新的线程,类似于用
fork()
创建子进程
pthread_create()原型
#include <pthread.h>
int pthread_create(
pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg
);
pthread_create()返回值
如果操作成功,返回0;如果操作失败,返回错误值
pthread_create()参数解释
pthread_t *thread
表示新创建的线程ID 指向的内存单元
const pthread_attr_t *attr
表示线程属性,通常为 NULL
void *(*start_routine) (void *)
函数指针,新创建的线程将从start_routine函数的地址开始运行
void *arg
若start_routine函数需要参数,将参数放入结构中并将地址作为arg传入,通常为NULL
pthread_create()编程实例
void * thread1(void *arg){ //定义thread1函数指针,作为start_routine
pthread_t thid=pthread_self();
printf("Current thread's ID is %lu\n", thid);
}
int main(){
pthread_t tid;
if( (tid = pthread_create(&tid,NULL,thread1,NULL) != 0){ //创建一个新的进程
perror("thread create failed");
exit(1);
}
}
3.3 pthread_exit()
pthread_exit()功能
结束当前线程
pthread_exit()原型
#include <pthread.h>
void pthread_exit(void *retval);
pthread_exit()返回值
该函数无返回值
pthread_exit()参数解释
void *retval
表示线程退出状态,通常为NULL
pthread_exit()编程实例
void * thread1(void *arg){
pthread_exit(NULL); //退出当前线程
}
exit() & pthread_exit()
-
在任何线程里使用
exit()
,都会导致进程终止,使得 其他线程还未完成当前任务,就被终止 -
因此,在多线程环境中,应 **少用或不使用
exit()
,而是用pthread_exit()
,将单个线程
3.4 pthread_join()
pthread_join()功能
阻塞主线程,获取子线程退出状态,类似于用
waitpid()
阻塞父进程并且获得子进程退出状态
pthread_join()原型
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
pthread_join()返回值
如果操作成功,返回0;如果操作失败,返回错误值
pthread_join()参数解释
pthread_t thread
表示想要获取退出状态的子线程的ID
void **retval
表示指向 指定线程的退出码的 二级指针 (类似于wait(&status)
中的&status
)
pthread_join()编程实例
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void* thread_run(void* arg)
{
sleep(3);
printf("thread %lu is running\n",pthread_self());
}
int main()
{
pthread_t tid;
pthread_create(&tid, NULL, thread_run,NULL);
pthread_join(tid,NULL); //主线程"main"会阻塞,直到tid线程执行完毕
printf("main thread exit\n");
return 0;
}
4. 多线程同步的概念
- 线程最大的特点是资源的共享性,然而多个线程同时访问某个资源时(如全局变量),如果这些线程的读写操作发生冲突,就会导致该资源的状态出现混乱
- Linux系统提供了多种方式处理线程间的同步问题,其中最常用的有 互斥锁、条件变量 和 异步信号。
5. 互斥锁的相关函数
互斥锁通过锁机制来实现线程的同步。在同一时刻它通常只允许一个线程执行关键部分的代码
5.1 pthread_mutex_init()
pthread_mutex_init()功能
初始化指定的互斥锁
pthread_mutex_init()原型
//动态初始化
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr);
//静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEXT_INITIALIZER;
pthread_mutex_init()返回值
如果操作成功,返回0;如果操作失败,返回错误值
pthread_mutex_init()参数解释
pthread_mutex_t *mutex
表示指定的互斥锁的地址
const pthread_mutexattr_t *mutexattr
表示互斥量属性,通常为NULL
pthread_mutex_init()编程实例
pthread_mutex_t mutex; //创建一个互斥锁mutex
pthread_mutex_init(&mutex,NULL) //动态初始化该互斥锁
动态初始化 VS 静态初始化
动态初始化 在堆中创建,不用时需要删除以释放内存
静态初始化 在静态存储区,初始化之后直接使用,不用时也不需要删除
5.2 pthread_mutex_lock()
pthread_mutex_lock()功能
若指定的互斥锁处于开锁状态,则锁上该锁;若处于上锁状态,则阻塞线程,直至该锁被打开,再上锁
pthread_mutex_lock()原型
int pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_lock()返回值
如果操作成功,返回0;如果操作失败,返回错误值
pthread_mutex_lock()参数解释
pthread_mutex_t *mutex
表示指定的互斥锁的地址
pthread_mutex_lock()编程实例
if (pthread_mutex_lock(&mutex) != 0); //将mutex互斥锁给锁上
perror("lock mutex failed\n")
5.3 pthread_mutex_unlock()
pthread_mutex_unlock()功能
若指定的互斥锁处于上锁状态,则打开该锁
pthread_mutex_unlock()原型
int pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread_mutex_unlock()返回值
如果操作成功,返回0;如果操作失败,返回错误值
pthread_mutex_unlock()参数解释
pthread_mutex_t *mutex
表示指定的互斥锁的地址
pthread_mutex_unlock()编程实例
if (pthread_mutex_unlock(&mutex) != 0); //将mutex互斥锁给打开
perror("unlock mutex failed\n")
5.4 pthread_mutex_destroy()
pthread_mutex_destroy()功能
销毁指定的互斥锁
pthread_mutex_destroy()原型
int pthread_mutex_destroy(pthread_mutex_t *mutex)
pthread_mutex_destroy()返回值
如果操作成功,返回0;如果操作失败,返回错误值
pthread_mutex_destroy()参数解释
pthread_mutex_t *mutex
表示指定的互斥锁的地址
pthread_mutex_destroy()编程实例
pthread_mutex_destroy(&mutex) //动态初始化该互斥锁
6. 条件变量的相关函数
-
条件变量是利用线程间共享的全局变量进行同步的一种机制。
-
条件变量宏观上类似if语句,符合条件就能执行某段程序,否则只能等待条件成立。
-
条件变量不是一把锁,它实质上一个类似信号的东西,与锁相互配合使用,因为锁所能达到的功能就只有加锁和解锁,并不能实现线程之间的一些关联,于是条件变量就出现了,与锁相互配合使用。这与共享内存与信号量配合使用有些许相似之处。
实验作业
createthread.c实践
源代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int * thread(void * arg) {
pthread_t newthid;
newthid = pthread_self()