前言
一、多线程基础函数
1. pthread_create
创建新的线程。
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
参数说明:
- thread: 用于存储新线程的ID。
- attr: 线程属性,通常使用NULL表示默认属性。
- start_routine: 线程的入口函数,线程将从该函数开始执行。
- arg: 传递给线程入口函数的参数。
pthread_t 的具体实现可能因不同的操作系统而有所变化,但在 Linux 中,它通常是一个整数类型(例如 unsigned long int 或 unsigned int)
2. pthread_self
函数返回当前线程的线程标识符(pthread_t 类型)。
pthread_t pthread_self(void);
线程标识符 tid ,用于在其他线程或线程管理函数中标识当前线程。
3. pthread_exit
用于终止当前单一线程的执行并返回一个指定的退出状态。
void pthread_exit(void *retval);
函数参数:
- retval:指向线程的退出状态的指针。可以是指向任何类型的指针,表示线程退出时传递的信息。
通过 pthread_exit,线程可以返回一个指定的退出状态,以便创建者线程或进程可以通过 pthread_join 等函数获取该状态
。
注意
:
在多线程环境中,不使用 exit 函数,取而代之使用 pthread_exit 函数,将单个线程退出。
因为 在 任何线程里 exit 会导致进程退出,这样会使其他线程为工作就结束。主线程退出不能使用 return 或 exit。
4. pthread_join
用于阻塞等待指定的线程终止,并获取该线程的退出状态。
它允许一个线程等待另一个线程的完成,以便协调线程的执行顺序和获取线程的返回结果。
int pthread_join(pthread_t thread, void **retval);
函数参数:
- thread:要等待的线程的线程标识符(pthread_t 类型)。
- retval:一个指针的指针,用于存储被等待线程的退出状态。
函数说明:
(1). pthread_join 函数阻塞当前线程,直到指定的线程终止。
(2). 当所等待的线程终止后,调用 pthread_join 的线程将被唤醒并继续执行。
(3). pthread_join 函数返回后,调用线程可以通过 retval 参数获取被等待线程的退出状态
。
(4). 被等待的线程在终止时必须使用 pthread_exit 函数返回一个退出状态
,以便被等待线程能够获取到退出状态。
示例代码:
#include <stdio.h> #include <pthread.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> void* my_pthread (void *arg) { int *val = (int*)arg; printf("*val = %d\n",*val); sleep(2); printf("pid = %d, tid = %ld\n", getpid(),pthread_self()); // 获取线程id pthread_exit((void*)val); // 结束线程,并返回退出状态 } int main(void) { pthread_t tid; int ret; int val = 10; int *threaf_status; printf("main : pid = %d\n", getpid()); ret = pthread_create(&tid,NULL, my_pthread, (void*)&val); // 创建线程 if(ret != 0) { printf("pthread_create err\n"); } pthread_join(tid, (void **)&threaf_status); // 阻塞等待回收线程的状态 printf("status : %d\n", *threaf_status); pthread_exit(NULL); }
5. pthread_cancel
用于向指定线程发送取消请求,请求线程终止执行。
int pthread_cancel(pthread_t thread);
取消点 (Cancellation Point):
取消点是一个线程能够响应取消请求的特定函数调用点。
标准的 POSIX 函数(例如 sleep、read、write 等)都是取消点,线程在这些函数调用时能够接收取消请求。
线程也可以使用 pthread_testcancel
函数主动检查取消请求,并在适当的地方终止自己的执行。
6. pthread_detach
用于将一个已经创建但还未被其他线程回收的线程标记为可分离状态,以便操作系统在线程终止时自动回收线程资源。
int pthread_detach(pthread_t thread);
在线程创建之后,但在其他线程调用 pthread_join 之前,可以调用 pthread_detach 来将线程设为分离状态。
可分离状态的线程可以在终止时自动释放系统资源,无需其他线程显式回收资源。这样可以避免对线程进行 pthread_join 调用等待线程结束。
二、线程间的共享数据
线程默认共享数据段,代码段等地址空间,常用的是全局变量。
线程共享 全局变量,静态变量, 文件描述符,动态分配的堆内存,数据结构。
对于共享资源的访问需要考虑线程安全性,使用适当的同步机制(如互斥锁、条件变量、读写锁等)来避免数据竞争和不一致性的问题。
#include <stdio.h> #include <pthread.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> int num = 20; void* my_pthread (void *arg) { num = 322; printf("2 : num = %d\n", num); pthread_exit(NULL); // 结束线程 } int main(void) { pthread_t tid; int ret; printf("1 : num = %d\n", num); ret = pthread_create(&tid,NULL, my_pthread, NULL); // 创建线程 if(ret != 0) { printf("pthread_create err\n"); } sleep(1); printf("3 : num = %d\n", num); pthread_exit(NULL); }
三、多线程 ,进程对比
多线程 | 进程 |
pthread_create() | fork() |
pthread_self() | getpid() |
pthread_exit() | exit() |
pthread_join() | wait() / waitpid() |
pthread_cancel() | kill() |
多线程和进程是并发编程中的两个重要概念,它们有着不同的特点和应用场景。下面是它们之间的比较:
- 进程(Process):是操作系统中的一个执行单位,具有独立的内存空间、代码和数据,可以拥有多个线程。
线程(Thread):是进程中的一个执行流程,共享进程的内存空间和资源,每个线程有自己的栈空间,但代码和数据是共享的。
资源占用: - 进程:每个进程都有独立的地址空间和系统资源,包括内存、文件描述符、CPU等。创建和销毁进程的开销较大。
线程:线程共享进程的地址空间和系统资源,包括内存、文件描述符等。创建和销毁线程的开销相对较小。
切换开销: - 进程:进程切换的开销较大,需要保存和恢复整个进程的上下文。
线程:线程切换的开销较小,因为线程共享进程的地址空间和资源,只需要保存和恢复线程的上下文。
通信方式: - 进程:不同进程之间的通信需要使用进程间通信(IPC)机制,如管道、消息队列、共享内存等。
线程:线程之间可以通过共享内存、全局变量等直接进行通信,不需要额外的通信机制。
可靠性: - 进程:由于进程拥有独立的地址空间,一个进程的崩溃不会影响其他进程。
线程:由于线程共享进程的地址空间,一个线程的崩溃可能会导致整个进程的崩溃。