6.线程创建
线程概念
线程和进程在使用资源、创建开销以及通信方式上存在显著差异。
首先,进程是系统资源分配的独立单位,每个进程拥有自己的地址空间,而线程则共享所隶属进程的地址空间。这意味着不同进程之间的资源如内存堆、栈是不能直接共享的,而同一进程内的多个线程可以直接访问这些共享资源。
其次,进程相较于线程有更大的创建和管理开销。因为进程有独立的地址空间,所以操作系统在创建或销毁进程时需要较大的系统资源开销。而线程作为调度的基本单位,其创建和上下文切换的开销要小得多,这也是为什么线程被称为轻量级进程的原因。
最后,由于线程间共享内存空间,它们之间的通信和数据共享更为简单直接。进程间则通常需要借助于进程间通信(IPC)机制,如管道、消息队列、共享内存等来完成数据交换。
线程特点
- 通常线程指的是共享相同地址空间的多个任务
- 使用多线程的好处
- 大大提高了任务切换的效率
- 避免了额外的TLB & cache的刷新
线程共享资源
一个进程中的多个线程共享以下资源:
- 可执行的指令
- 静态数据
- 进程中打开的文件描述符
- 当前工作目录
- 用户ID
- 用户组ID
线程私有资源
- 线程ID (TID)
- PC(程序计数器)和相关寄存器
- 堆栈
- 错误号 (errno)
- 优先级
- 执行状态和属性
线程创建
pthread_create 函数
原型:
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
功能:创建一个新的线程,并使其开始执行指定的函数。
参数:
pthread_t *thread
:指向一个pthread_t
类型的指针,用于存储新创建线程的标识符。const pthread_attr_t *attr
:指向一个pthread_attr_t
类型的指针,用于设置线程的属性。如果传入NULL,则使用默认属性。void *(*start_routine) (void *)
:指向一个函数指针,该函数将在新线程中执行。函数的返回类型为void *
,参数为void *
。void *arg
:传递给start_routine
函数的参数。返回值:
- 成功时返回0
- 失败时返回错误码。
注意:创建线程后,若主进程运行结束,它创建的线程也会随之结束。
所以,要在主进程预留一段时间等待线程结束,避免线程中断。
错误解决
使用pthread_create函数时出现的错误:
test_createP.c:(.text+0x4b):对‘pthread_create’未定义的引用
collect2: error: ld returned 1 exit status这个为链接错误,因为pthread_create函数的库为动态链接库
解决:在编译时加上 -lpthread
例:$gcc test.c -lpthread
线程结束
pthread_exit 函数
原型:
#include <pthread.h> void pthread_exit(void *retval);
功能:结束当前线程,线程私有资源被释放,并将返回值传递给其他线程
参数:
void *retval
:指向一个指针,用于存储线程的返回值。该值可以被其他线程通过pthread_join()
函数获取。
查看线程
pthread_self 函数
原型:
#include <pthread.h> pthread_t pthread_self(void);
功能:获取调用线程(自身所处线程)的标识符。
返回值:返回调用线程的标识符,类型为
pthread_t
。
线程传参
线程间传参有两种方式,值传递和地址传递。
示例:
arg1
为地址传递,arg2
为值传递#include <stdio.h> #include <pthread.h> #include <unistd.h> int * test_pth1(void *arg) { printf("p1 tid = %lu\n",pthread_self());//线程ID printf("p1 pid = %d\n",getpid());//进程ID printf("p1 arg = %d\n",*(int *)arg);//传的参数 pthread_exit(NULL);//退出线程 } int * test_pth2(void *arg) { printf("p2 tid = %lu\n",pthread_self());//线程ID printf("p2 pid = %d\n",getpid());//进程ID printf("p2 arg = %d\n",(int)arg);//传的参数 pthread_exit(NULL);//退出线程 } int main(int argc, const char *argv[]) { pthread_t tid;//线程ID int ret; int arg1,arg2; /*创建线程*/ for(arg1=0; arg1<4; arg1++) {//地址传递 ret = pthread_create(&tid,NULL,(void *)test_pth1,(void *)&arg1); } sleep(1); for(arg2=0; arg2<4; arg2++) {//值传递 ret = pthread_create(&tid,NULL,(void *)test_pth2,(void *)arg2); } printf("main tid%lu\n",tid); sleep(1);//等待线程结束 return 0; }
运行结果:
地址传递arg的值都为4,而值传递的arg值为创建线程时传入的值
地址传递的值会随着地址指向的数改变而改变,而线程创建的速度要比线程运行的速度要快,四个线程创建完后arg 的值为4,然后线程才开始运行,所以都打印出arg=4。