一、概念
线程:线程是一个轻量级的进程
二、线程的创建
1、线程的空间
(1)进程的空间包括:系统数据段、数据段、文本段
(2) 线程位于进程空间内部
(3) 栈区独享、与进程共享文本段、数据段、堆区
2、线程与进程的关系
进程:进程是操作系统资源分配的最小单元(进程:文本段+数据段+系统数据段)
线程:线程是CPU任务调度的最小单元
(1)每个线程独立拥有8M(默认)栈空间
(2)其余的文本段、数据段、堆区都是与进程及进程内的其余线程共享的
三、线程的调度
(1)线程的调度等价于进程的调度(宏观并行,微观串行)
(2)线程是CPU任务调度的最小单元
四、线程的消亡
(1)与进程消亡保持一致
(2)僵尸线程:等到线程结束需要回收线程空间,否则会产生僵尸线程
五、多进程vs多线程
1、执行效率
多线程 > 多进程
线程 间任务的切换是在同一片进程空间内部完成任务切换,资源开销小
进程 间任务的切换需要映射到不同的物理地址空间,频繁切换资源开销大
2、多任务间通信的实现
多线程 > 多进程
多线程 拥有共享空间(多个线程位于一个进程内,共享该进程的数据区、文本区),通信更加方便
多进程 没有共享空间(进程间是独立的),需要更为复杂的方法实现多进程间通信
3、多任务通信机制的复杂程度
多线程 > 多进程
多线程 操作全局变量会引入资源竞争,需要加锁来解决
多进程 没有资源竞争
4、安全性
多进程 > 多线程
线程 异常崩溃导致进程崩溃,该进程中的其余线程异常结束
进程 异常结束,不会影响其余进程
六、线程相关的函数接口
pthread_create == fork
pthread_exit == exit
pthread_join == wait
1、pthread_creat
man 3 pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
功能:
在调用函数的进程中创建一个线程
参数:
thread:存放线程ID空间的首地址
attr:线程的属性
start_routine:线程函数的入口
void *(*start_routine) (void *)函数指针,返回值类型void*,参数void*,*start_routine:函数首地址(函数名本身就是函数首地址)
arg:传入线程函数的参数
返回值:
成功返回0
失败返回错误数字
#include <stdio.h>
#include "public.h" //头文件中包含pthread_creat的头文件,但是编译时还需使用 -lpthread(链接头文件)
//cc src/main.c -o ./build/a.out -I./include -lpthread
void *thread_fun(void *arg)
{
printf("线程(TID:%#x)开始执行\n", (unsigned int)pthread_self()); //pthread_self获取线程ID
return NULL;
}
int main(int argc, const char **argv)
{
pthread_t tid1;
pthread_t tid2;
pthread_t tid3;
int ret = 0;
ret = pthread_create(&tid1, NULL, thread_fun, NULL);
if (ret != 0)
{
ERR_MSG("fail to pthread_create");
return -1;
}
ret = pthread_create(&tid2, NULL, thread_fun, NULL);
if (ret != 0)
{
ERR_MSG("fail to pthread_create");
return -1;
}
ret = pthread_create(&tid3, NULL, thread_fun, NULL);
if (ret != 0)
{
ERR_MSG("fail to pthread_create");
return -1;
}
while (1)
{
}
return 0;
}
2、pthread_exit
man 3 pthread_exit
void pthread_exit(void *retval);
功能:
线程退出
参数:
retval:线程退出的值
返回值:
缺省
3、pthread_join
man 3 pthread_join
int pthread_join(pthread_t thread, void **retval);
功能:
回收线程空间
参数:
thread:要回收的线程的ID
retval:存放线程结束状态指针空间的首地址
返回值:
成功返回0
失败返回错误码
注意:
1.pthread_join具有阻塞功能,线程不结束,一直等到线程结束,回收到线程空间再继续向下执 行
2.pthread_join具有同步功能
#include <stdio.h>
#include "public.h"
#if 0
int main(int argc,const char** argv)
{
pthread_t tid1;
pthread_t tid2;
pthread_t tid3;
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
pthread_create(&tid3, NULL, thread3, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
return 0;
}
#endif
#if 0
void *thread1(void *arg)
{
printf("线程1(TID:%#lx)正在执行\n", pthread_self());
return NULL;
}
void *thread2(void *arg)
{
printf("线程2(TID:%#lx)正在执行\n", pthread_self());
return NULL;
}
void *thread3(void *arg)
{
printf("线程3(TID:%#lx)正在执行\n", pthread_self());
return NULL;
}
int main(int argc, const char **argv)
{
pthread_t tid[3];
int i = 0;
int a[3] = {1, 2, 3};
void *(*pfun[3])(void *) = {thread1, thread2, thread3}; //函数指针数组
for (i = 0; i < 3; i++)
{
pthread_create(&tid[i], NULL,pfun[i],NULL);
}
for (i = 0; i < 3; i++)
{
pthread_join(tid[i], NULL);
}
#endif
#if 1
void *thread(void* arg)
{
int* pnum = arg;
printf("线程%d(TID:%#lx)正在执行\n", *pnum, pthread_self());
return NULL;
}
int main(int argc, const char **argv)
{
pthread_t tid[3];
int i = 0;
int a[3] = {1, 2, 3};
for (i = 0; i < 3; i++)
{
//pthread_create(&tid[i], NULL,thread,&i); //error 所有线程共享同一个 i 的地址,因此当线程函数 thread 访问 &i 时,获取的值可能是循环结束后 i 的最终值(即 3),而不是创建线程时的值。这会导致所有线程可能接收到相同的值3,而不是预期的 0、1、2
pthread_create(&tid[i], NULL,thread,&a[i]); //right 数组 a 的值是固定的({1, 2, 3}),每个线程会接收到数组 a 中对应索引的值。由于每个线程接收的是数组 a 中不同元素的地址,线程函数 thread 可以正确地访问到 1、2、3 这些值
}
for (i = 0; i < 3; i++)
{
pthread_join(tid[i], NULL);
}
#endif
return 0;
}