当前位置: 首页 > news >正文

C++学习之线程

目录

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,没有独立的进程地址空间。(与其他线程共享) —— 最小执行单位。

![1584326812185](课堂笔记10.assets/1584326812185.png)

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

![1584327010849](课堂笔记10.assets/1584327010849.png)

- 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() |                |
 

http://www.dtcms.com/a/113590.html

相关文章:

  • [Android安卓移动计算]:新建项目和配置环境步骤
  • 力扣DAY35 | 热100 | LRU缓存
  • 在windows环境下通过docker-compose脚本自动创建mysql和redis
  • SQL Server常见问题的分类解析(二)
  • 分治-归并排序-逆序对问题
  • 计算机视觉图像处理基础系列:滤波、边缘检测与形态学操作
  • 小迪安全110-tp框架,版本缺陷,不安全写法,路由访问,利用链
  • Android使用OpenGL和MediaCodec渲染视频
  • AI浪潮下,“内容创作平台”能否借势实现内容价值跃升?
  • Turtle图形化编程知识点汇总:让编程更有趣
  • IDEA 2024.3.5 中修改 web.xml 的 Servlet 版本(比如从 4.0 修改为 5.0)
  • I.MX6ULL开发板与linux互传文件的方法--NFS,SCP,mount
  • AbstractBeanFactory
  • 基于SSM的车辆管理系统的设计与实现(代码+数据库+LW)
  • kd树和球树
  • Java中使用OpenCV实现怀旧滤镜时遇到的UnsatisfiedLinkError问题及解决方案
  • 一文读懂 MCP!
  • chromadb
  • Swift 扩展
  • 微服务架构与中台的关系
  • 高通camx ThreadManager
  • 【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 的未来:从微服务到云原生的演进
  • Hyperlane框架:下一代高性能Rust Web框架 [特殊字符]
  • 学习笔记,DbContext context 对象是保存了所有用户对象吗
  • ring语言,使用vscode编辑器
  • AtCoder Beginner Contest 399 D,F 题解
  • 对迭代器模式的理解
  • Arduino示例代码讲解:Knock Sensor 敲击感知器
  • 每日一题(小白)模拟娱乐篇14
  • BN测试和训练时有什么不同, 在测试时怎么使用?