提示:全文仅是我(初学者)的笔记,可能有较多错误,请辩证观看,有错误请帮忙指出,谢谢。
什么是线程
首先要明确线程和进程之前的区别
- 比如电脑运行微信时,就可以把它当成一个进程,但是这个进程包括了许多小task,比如界面的显示、消息的接收、输入登录密码等,这些任务是进程还是线程呢?显然这些不是进程,因为进程是直接参与内核的调度,地址空间也不是共用的,每次切换进程都要耗费较多的资源。但这些任务要高度共享资源,且要方便切换,可以看做是“同时”运行的,所以就引出了线程。由一个或者多个线程构成了一个完整的进程。
进程的特点
- 进程有独立的地址空间;
- 每个进程都参与内核的调度,互不影响;
- 在切换时耗费较多的系统资源。
线程的特点
- 同一个进程中的线程,共享地址空间;
- 提高了任务切换的效率;
- 在调度任务时,由于线程之间许多是共享的,就无需cache频繁的切换,而如果是进程,则需要频繁切换。
线程之间哪些东西是共享的,哪些又是私有的?
共享资源:
- 可执行的指令
- 用户ID、用户组ID
- 静态数据,当前工作目录等。
私有资源:
- 线程的ID
- 程序计数器(PC)和相关寄存器
- 堆栈
线程创建例程
# include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*routine)(void*), void *arg);
- 成功时返回0,失败时返回错误码
- thread 线程对象
- attr 线程属性, NULL表示默认属性
- routine线程执行的函数
- arg 传递给routine的参数,参数是void*
#include <pthread.h>
#include <stdio.h>
#include <string.h>
void * fun1(void *arg){
printf("this is thread fun1\n");
sleep(50);
}
int main(){
pthread_t tid;
int err;
err = pthread_create(&tid, NULL, fun1, NULL);
if (err !=0 ){
printf("create thread:%s\n", strerror(err));
return -1;
}
sleep(50);
}
- 线程编译命令要加lpthread
- gcc -o pthread pthread.c -lpthread
- 查看线程:ps -elf |grep cthread
线程回收
- 线程在调用结束后不会自动释放内存,需要进行回收,释放内存。
- #include
- int pthread_join(pthread_t thread, void *retval)
- 成功时返回0,失败时返回错误码
- thread是指要回收的线程对象
- *retval接收线程thread的返回值
- 此函数调用线程阻塞,直到thread结束。
线程间通信
为什么线程间要通信
- 之前说过线程共享同一进程的地址空间,且各个线程之间存在频繁的数据交换。多线程是并发运行的,所以在交换数据时要引入同步或者互斥机制。举个例子说明,如果线程1在往某个变量写入值,而线程2要从这个变量中取出值,那么线程2只能等线程1往此变量中写入了值后,再取值,而两个线程是并发运行的,为了写值和读值不出现错误,就引入同步或者互斥机制。
什么是同步机制
- 同步指的是多个任务按照约定的先后顺序互相配合完成一个大任务。
- 由信号量决定线程是继续运行还是阻塞等待。
- 信号量是一个受保护的变量,只能通过:初始化、P操作(申请资源)、V操作(释放资源)来进行操作。
- 常用的信号操作函数
- int sem_init(sem_t *sem, int pshared, unsigned int value); *sem表示初始化信号量, pshared 0/1表示对线程/进程间通讯用, value 初始量。
- int sem_wait(sem_t *sem) P操作 让信号量-1
- int sem_post(sem_t *sem) V操作 让信号量+1
- 举个例子:一个人往一个容器里面放东西,一次只能放一个,里面只能放下两个(初始量),另一个人从这个容器中往外面拿,一次只能拿一个。当取的这个人往外面拿了一个,放的这个人接收到容器还能放下的信号,继续往容器里面放。P 操作就是告诉还能放(信号量-1),V 操作告诉放不了(信号量+1)。