No.1 线程基础
一、什么是线程:
1. 进程是os资源分配的基本单位 : 资源
2. 线程是os调度的基本单位 : 代码
3. windows跟linux多线程类似,但是函数名不同,方式不同。
- 区别:
- 一个进程里面可以有多个线程,同一个进程中的多个线程共享进程资源
- 本质上进程和线程的没有区别
- 2.2版本的linux内核中,没有线程,只有进程,并且进程数量有限,共4096个
- 2.4版本开始,内核中有了线程的概念,线程个数无线
- 线程和进程都是进程的结构体
二、如何创建线程:
一个进程种至少存在一个进程 主进程 main函数
pthread_create函数 创建并立刻启动线程
#include <pthread.h>
int pthread_create(
pthread_t *thread, //线程id 作为返回值来使用
const pthread_attr_t *attr, //线程属性
void *(*start_routine) (void *),//线程函数 是一个函数指针
void *arg); //线程函数的参数
Compile and link with -pthread.
由于函数声明在pthread.h中,但是函数定义在pthread这个动态库中,因此编译的时候需要链接这个库
- 函数指针: 函数名就是函数的地址
对于第三个参数,如果没有将函数指针强转为要求的类型,会报错,但是可以运行。gcc pthread.c -l pthread
编译的时候需要带上库 - C与C++的区别:
- C不做强制类型检查,
- C++做强制类型检查
pthread.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> //sleep
//void *(*start_routine) (void *)
//两种解决方案
//一、将函数定义为符合它的要求的函数 void*()(void*)
//二、强制类型转换
typedef void* (*A)(void*);//给线程函数指针类型取别名 A
//A指针类型是一个符合要求的指针类型
//原void f1()
//一、void* f1(void* a)
void f1()
{
for(int i=0;i<10;i++)
{
printf("子线程:%d\n",i);
sleep(1);
}
printf("分支线程结束\n");
}
int main()
{
int n = 0;
//pid可以直接为NULL,也可定义一个pthread_t的变量
pthread_t pid;
pthread_create(&pid,NULL,(A)f1,NULL);
for(int i=0;i<10;i++)
{
printf("主线程:%d\n",i);
sleep(1);
}
for(int i=0;i<5;i++)
{
printf("主线程再来一会:%d\n",i);
sleep(1);
}
printf("主线程结束\n");
return 0;
}
三、线程的结束:
1. 自然返回
+ 线程函数返回
2. 主线程要结束了
+ 主线程会结束所有的分支线程
3. 自己结束自己
+ pthread_exit
+ `void pthread_exit(void *retval);`
4. 被其他线程干掉
+ pthread_cancel
+ `int pthread_cancel(pthread_t thread);`
thread 就是创建时的第一个参数
四、线程的参数和返回值
- 参数
多个参数可以创建一个结构体,然后将结构体实例化后传地址放在线程创建的第四个参数位置。在线程函数中强转传入的地址就可以正常使用了。
struct Student
{
char name[20];
int age;
double score;
};
void* f(void* p)
{
struct Student* ps = (struct Student*)p;
//然后就可以对ps正常使用
}
int main()
{
struct Student s={"张三",13,33.33};
pthread_create(NULL,NULL,f,&s);
}
-
返回值
主线程结束前,会结束所有的分支线程。
等待某个线程结束:
+ wait
+ pthread_jionint pthread_join( pthread_t thread, void **retval); //获取返回值 void* 就是int,所以它不仅可以接受单个的返回值,也可以像传结构体那样接受结构体,即多个返回值不过在函数返回时需要传地址。
五、什么时候使用多线程:需要并发的时候
- 并发又叫并行,它跟串行是反面。
- 串行是单任务,并发是多任务。
- 并不是并发一定会比串行快,如果计算任务超过了cpu的计算,那么有对对不同线程的调度也需要消耗时间,因此相对来说还会比串行效率慢。
六、临界数据
- 同一个进程内的多个线程共享进程的资源,那么必定会造成资源的征用
- 多个线程同时访问同一块内存段,会造成临界数据、脏的问题
七、线程同步:解决临界数据脏的问题
应用层:
原子锁
自旋锁
信号量 前面的文章里有
内核态:
读写锁
互斥量
临界变量