目录
1.进程和线程的概念
2.线程内核三级映射
3.线程优缺点
4.创建线程和获取线程ID的函数
5.创建子线程
6.循环创建N个子线程
7.子线程传参地址错误演示分析
8.主、子线程共享全局变量、堆空间
9.线程退出
10.pthread join回收线程退出值
11.pthread_cancel
12.杀死线程pthread_cancel
13.进程线程对比
14.线程属性简介
1.进程和线程的概念
- Linux 系统中,线程 LWP 称之为:轻量级的进程。
- 进程:有独立的进程地址空间, 有独立的 pcb。 —— 最小资源分配单位。
- 线程:有独立的pcb,没有独立的进程地址空间。(与其他线程共享) —— 最小执行单位。

- 一个创建了线程的进程,本身也沦落 为线程。

- LWP 号: cpu 划分时间片依据。 —— 线程 最小执行单位。
- 查看LWP号命令: ps -Lf 进程pid
2.线程内核三级映射
3.线程优缺点
- 优点:
- 并发性强。
- 开销小。
- 数据通信方便。
- 缺点:
- 库函数,稳定性差。
- 调试、编写困难
- 对信号支持差。
- 结论:既能使用进程开发,也能使用线程开发的程序,首选 线程。
4.创建线程和获取线程ID的函数
```c
#include <pthread.h>
pthread_t pthread_self(void); // 获取线程id, 在进程内部标识线程身份。
返回线程id。 没有错误!
```
5.创建子线程
```c
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参1: 传出参数,新子线的 线程id
参2: 线程属性。默认传 NULL, 表使用默认属性。
参3: 子线程回调函数。pthread_create 调用成功,该函数会被自动调用起来。
参4: 参3 的参数。
返回值:
成功:0
失败:直接返回错误号!
6.循环创建N个子线程
```c
// 子线程主函数
void *tfn(void *arg)
{
printf("tfn : pid = %d, pthread_id = %lu\n", getpid(), pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid;
// 创建子线程
int ret = pthread_create(&tid, NULL, tfn, NULL);
if (ret != 0)
fprintf(stderr, "pthread_create err:%s\n", strerror(ret));
printf("main : pid = %d, pthread_id = %lu\n", getpid(), pthread_self());
sleep(1); // 给子线程执行时间
return 0; // 释放
7.子线程传参地址错误演示分析
8.主、子线程共享全局变量、堆空间
// 阻塞 回收线程。
int pthread_join(pthread_t thread, void **retval);
参1:待回收的线程id
参2:传出参数。回收的那个线程的 退出值。
进程中:main返回值:return 0、 exit(1) ---> int。 回收进程退出值 wait(int *)
线程中:线程返回值:pthread_exit --> void *。 回收线程退出值 pthread_join(void **)
返回值:
成功:0
失败:直接返回错误号!
```
9.线程退出
```c
// 子线程主题函数
void *tfn(void *arg)
{
sleep(5);
//return (void *)74;
pthread_exit((void *)"hello");
}
int main(int argc, char *argv[])
{
pthread_t tid;
//int *retval; // 用来存储子进程退出值
char *retval; // 用来存储子进程退出值
// 创建子线程
int ret = pthread_create(&tid, NULL, tfn, NULL);
if (ret != 0)
fprintf(stderr, "pthread_create err:%s\n", strerror(ret));
printf("----------------1\n");
// 回收子线程退出值
ret = pthread_join(tid, (void **)&retval);
if (ret != 0)
fprintf(stderr, "pthread_join err:%s\n", strerror(ret));
printf("child thread exit with %s\n", (char *)retval);
pthread_exit((void *)0); // 退出主线程
}
10.pthread join回收线程退出值
```c
struct thrd {
int var;
char str[256];
};
// 子线程主题函数
void *tfn(void *arg)
{
struct thrd *tval = (struct thrd *)arg; //malloc()
tval->var = 100;
strcpy(tval->str, "hello thread");
pthread_exit((void *)tval);
// return (void *)tval; // 也可以
}
int main(int argc, char *argv[])
{
pthread_t tid;
struct thrd arg, *retval;
// 创建子线程
int ret = pthread_create(&tid, NULL, tfn, (void *)&arg);
if (ret != 0)
fprintf(stderr, "pthread_create err:%s\n", strerror(ret));
// 回收子线程退出值
ret = pthread_join(tid, (void **)&retval);
if (ret != 0)
fprintf(stderr, "pthread_join err:%s\n", strerror(ret));
printf("child exit with: var = %d, str= %s\n", retval->var, retval->str);
// free();
pthread_exit((void *)0); // 退出主线程
}
```
11.pthread_cancel
- 与进程类似,线程结束时,也有 “僵尸线程” 产生。消耗系统资源。
```c
int pthread_detach(pthread_t thread); // 设置线程为分离态。
参:待设置为分离的线程id
```
- 设置为 分离态的 线程,在终止时,会自动清理 pcb 内核残留。
- 对于已经分离的线程,使用 pthread_join() 不能正常回收。不能获取线程退出值。
12.杀死线程pthread_cancel
``c
int pthread_cancel(pthread_t thread);
参:待杀死的线程id
```
1. 被 pthread_cancel() 杀死的线程,在使用 pthread_join() 回收,得到的退出值 -1。
2. pthread_cancel() 杀死线程必须要到达一个 “取消点” (保存点), 才能生效。否则无法杀死线程。
- 应该在被cancel的线程中,调用 pthread_testcancel() 函数 来 添加 “取消点” (保存点)。
13.进程线程对比
1. return
2. pthread_exit()
3. pthread_cancel() 需要 “保存点”。 —— 进内核,即可得到。
14.线程属性简介
| 线程控制原语 | 进程控制原语 |
| ---------------- | -------------- |
| pthread_create() | fork() |
| pthread_self() | getpid() |
| pthread_exit() | exit() |
| pthread_join() | wait/waitpid() |
| pthread_cancel() | kill() |
| pthread_detach() | |