Linux 【C编程】 引入线程,线程相关函数

1.线程的引入

1.1使用线程同时读取键盘和鼠标

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
// 读取键盘输入的线程函数
void *keyboardThread(void *arg) {
    char c;
    while (1) {
        // 从键盘读取一个字符
        c = getchar();
        // 打印从键盘读取的字符
        printf("键盘输入: %c\n", c);
    }
}

int  main(){
    int ret = -1;
    int fd = -1;
    char buf[200];
    pthread_t th  =-1;
    //k开一个子线程
    ret = pthread_create(&th,NULL,keyboardThread,NULL);

    //主进程读取鼠标输入

    memset(buf,0,sizeof(buf));
    //读取鼠标
    fd = open("/dev/input/mouse0",O_RDONLY);
    if(fd<0){
        perror("open:");
        return -1;
    }

    while(1){
        read (fd,buf,2);  //从鼠标读取两位
        printf("mouse读出来的内容是 :【%s】\n",buf);
    }
 
    return 0;

}

效果如下:
在这里插入图片描述
需要注意到的一点是:
使用了pthread库,因此编译时需要链接 -lpthread

1.2Linux中线程简介

  • 在Linux系统中,线程是一种轻量级的执行单元,与进程共享相同的地址空间。
  • 一个进程中可以有多个线程
  • 线程是参与内核调度的最小单元

1.3线程技术的优势

  • 并发执行: 线程使得程序可以同时执行多个任务,从而提高了系统的并发性。这允许程序在某个线程等待I/O操作完成的同时,其他线程可以继续执行,提高了系统的利用率。

  • 资源共享: 线程之间共享同一进程的地址空间,可以更方便地共享数据。这样,多个线程可以访问和修改相同的变量,共享同一份代码和静态数据,减少了资源的冗余使用。

  • 轻量级: 相较于进程,线程是轻量级的执行单元。线程的创建、销毁和切换成本较低,因此在需要频繁创建和销毁执行单元的场景中,线程更为适用。

  • 响应性: 多线程可以使程序更加响应用户输入和事件。例如,在图形用户界面(GUI)应用程序中,一个线程可以负责用户界面的更新,而另一个线程可以处理用户输入,使得应用程序看起来更为流畅。

  • 模块化设计: 通过将不同的任务分配给不同的线程,可以实现模块化的程序设计。每个线程负责一个特定的功能或任务,使得代码更易于理解、维护和扩展。

  • 性能提升: 在多核处理器上,多线程程序可以更好地利用硬件资源,从而提高程序的性能。并行执行的线程可以在多个核心上同时运行,加速任务的完成。

  • 异步编程: 线程可以用于实现异步编程模型,通过在后台执行任务,使得程序能够继续执行其他操作。这对于需要处理I/O操作或等待外部事件的场景非常有用。

  • 实时性: 一些实时系统需要在特定时间内完成任务,线程可以用于实现对任务的及时响应,确保实时性要求得到满足。

2.线程中常见函数

2.1 线程创建与回收

  • pthread_create

    POSIX 线程库中用于创建新线程的函数。该函数的原型如下:

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void*), void *arg);
  • thread:一个指向 pthread_t 类型的变量的指针,用于存储新线程的标识符。

  • attr:一个指向 pthread_attr_t 类型的变量的指针,用于设置新线程的属性,通常可以传入 NULL 以使用默认属性。

  • start_routine:是一个指向函数的指针,该函数定义了新线程的起始点。新线程将从这个函数开始执行。这个函数的签名应为 void* start_routine(void*)。

  • arg:是传递给 start_routine 函数的参数。

函数返回值为 0 表示成功创建新线程,非零值表示出现了错误。

  • pthread_join
    主线程等待(阻塞)回收子线程
#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);
  • thread:要等待的线程的标识符,通常由 pthread_create 返回得到。
  • retval:一个指向指针的指针,用于获取线程的返回值。如果不关心线程的返回值,可以将这个参数设置为 NULL。

可以说 pthread_create 与 pthread_join 成对出现的,一个负责创建一个负责回收
代码演示:

#include <stdio.h>
#include <pthread.h>

void *printMessage(void *arg) {
    char *message = (char *)arg;
    printf("%s\n", message);
    pthread_exit((void *)42);
}

int main() {
    pthread_t tid;
    char *message = "Hello from the new thread!";
    void *result;

    // 创建新线程
    int result_create = pthread_create(&tid, NULL, printMessage, (void *)message);
    if (result_create != 0) {
        fprintf(stderr, "Thread creation failed: %d\n", result_create);
        return 1;
    }

    // 等待新线程结束
    int result_join = pthread_join(tid, &result);
    if (result_join != 0) {
        fprintf(stderr, "Thread join failed: %d\n", result_join);
        return 1;
    }

    // 打印新线程的返回值
    printf("New thread returned: %ld\n", (long)result);

    return 0;
}
  • pthread_detach
    用于将一个线程标记为“分离状态”(detached state)。线程的分离状态决定了线程退出后系统是否会自动回收其资源。
int pthread_detach(pthread_t thread);

这个函数的参数 thread 是要分离的线程的标识符,通常由 pthread_create 返回得到。函数返回值为 0 表示成功,非零值表示出现了错误。
调用 pthread_detach 将线程标记为“分离状态”,这意味着线程结束后,系统会自动回收其资源。分离状态的线程不再需要主线程调用 pthread_join 来等待其结束,因为线程结束后,它的所有资源都会被自动回收。
一旦调用 pthread_detach 将线程标记为分离状态,就无法再将其改回非分离状态。这意味着在设置分离状态后,主线程无需显式等待该线程的结束。
代码演示:

#include <stdio.h>
#include <pthread.h>

void *printMessage(void *arg) {
    char *message = (char *)arg;
    printf("%s\n", message);
    pthread_exit(NULL);
}

int main() {
    pthread_t tid;
    char *message = "Hello from the detached thread!";

    // 创建新线程并设置为分离状态
    int result_create = pthread_create(&tid, NULL, printMessage, (void *)message);
    if (result_create != 0) {
        fprintf(stderr, "Thread creation failed: %d\n", result_create);
        return 1;
    }

    // 设置线程为分离状态
    int result_detach = pthread_detach(tid);
    if (result_detach != 0) {
        fprintf(stderr, "Thread detach failed: %d\n", result_detach);
        return 1;
    }

    // 主线程继续执行,不需要等待新线程结束

    return 0;
}

2.2线程取消

  • pthread_cancel
    用于请求取消另一个线程的执行。该函数的原型如下:
int pthread_cancel(pthread_t thread);

函数返回值为 0 表示成功发送取消请求,非零值表示出现了错误。

  • pthread_setcancelstate
    子线程设置自己是否可以被取消,新线程被创建之后,默认是可以被取消的
  • pthread_setcanceltype
    设置线程的取消类型。有两种类型:PTHREAD_CANCEL_DEFERRED 和 PTHREAD_CANCEL_ASYNCHRONOUS。前者表示线程将在取消点检查取消请求,后者表示线程将在任何时刻检查取消请求。默认情况下,取消类型是 PTHREAD_CANCEL_DEFERRED。

线程会在取消点检查取消请求。取消点是一些标准的库函数,例如 pthread_join、sleep 等。
线程在执行这些函数时,会检查是否有取消请求。
代码演示:

#include <stdio.h>
#include <pthread.h>

void *cancelExample(void *arg) {
    while (1) {
        printf("Thread is running...\n");
        sleep(1);  // 休眠是一个取消点
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t tid;

    // 创建新线程
    int result_create = pthread_create(&tid, NULL, cancelExample, NULL);
    if (result_create != 0) {
        fprintf(stderr, "Thread creation failed: %d\n", result_create);
        return 1;
    }

    // 主线程休眠一段时间后发送取消请求
    sleep(3);
    int result_cancel = pthread_cancel(tid);
    if (result_cancel != 0) {
        fprintf(stderr, "Thread cancel failed: %d\n", result_cancel);
        return 1;
    }

    // 主线程等待新线程结束
    pthread_join(tid, NULL);

    return 0;
}

2.3线程退出

  • pthread_exit
    pthread_exit 和 return 都用于结束线程的执行
void pthread_exit(void *retval);

pthread_exit 是一个线程库提供的函数,用于终止当前线程的执行。它可以传递一个指针作为线程的返回值,这个指针将被用作线程的退出状态。当调用 pthread_exit 时,线程的资源(如栈空间)不会被回收,需要通过其他线程调用 pthread_join 来等待并回收这个线程的资源。

  • pthread_cleanup_push
  • pthread_cleanup_pop
    pthread_cleanup_push 是 POSIX 线程库中的函数,用于将清理函数(cleanup handler)推入线程的清理处理栈。它通常与 pthread_cleanup_pop 配合使用,用于在线程退出时执行一些清理操作。
void pthread_cleanup_push(void (*routine)(void *), void *arg);

pthread_cleanup_push 将清理函数和参数推入线程的清理处理栈。在线程退出时,栈上的所有清理函数都会被依次执行。
代码演示:

#include <stdio.h>
#include <pthread.h>

void cleanupHandler(void *arg) {
    printf("Cleanup handler: %s\n", (char *)arg);
}

void *threadFunction(void *arg) {
    // 推入清理函数和参数到栈
    pthread_cleanup_push(cleanupHandler, "First cleanup");
    pthread_cleanup_push(cleanupHandler, "Second cleanup");

    // 线程的主要逻辑
    printf("Thread is running.\n");
    // 模拟线程取消点(Cancellation Point)
    sleep(2);

    // 弹出清理函数,但不执行
    pthread_cleanup_pop(0);
    // 弹出清理函数,并执行
    pthread_cleanup_pop(1);

    // 正常退出
    pthread_exit(NULL);
}

int main() {
    pthread_t tid;

    // 创建新线程
    int result_create = pthread_create(&tid, NULL, threadFunction, NULL);
    if (result_create != 0) {
        fprintf(stderr, "Thread creation failed: %d\n", result_create);
        return 1;
    }

    // 等待线程结束
    pthread_join(tid, NULL);

    return 0;
}

2.4获取线程ID

使用 pthread_self 函数来获取当前线程的线程标识符(Thread ID)。该函数的原型如下:


pthread_t pthread_self(void);

调用该函数就会返回调用线程的线程标识符

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/335100.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

可媲美Gen2的视频生成大一统模型;Vlogger根据用户描述生成分钟级视频;Vision Mamba提速2.8倍节省86.8%

本文首发于公众号&#xff1a;机器感知 可媲美Gen2的视频生成大一统模型&#xff1b;Vlogger根据用户描述生成分钟级视频&#xff1b;Vision Mamba提速2.8倍节省86.8% UniVG: Towards UNIfied-modal Video Generation Current efforts are mainly concentrated on single-obj…

00-Rust前言

问&#xff1a;为什么要近期想学习Rust? 答&#xff1a; Rust出来也是有一段时间了&#xff0c;从Microsoft吵着要重构他们的C"祖传代码"开始&#xff0c;Rust就披着“高效&#xff0c;安全”的头衔。而自己决定要学习Rust&#xff0c;是因为近期发现&#xff1a;与…

redisson的延时队列机制简述

概述 业务中经常会遇到一些延迟执行的需求&#xff1b;通常想到的都是rabbitmq或者rocketmq的延迟消息&#xff1b; 但是系统中不一定集成了mq&#xff0c;但为了控制分布式下的并发&#xff0c;一般redis都是有集成的&#xff1b; redis的key过期监听那个时间不准确&#xff…

C#,实用新型加强版的整数数组

疫苗要打加强针&#xff01;数组要用加强版&#xff01; 三连发 加强版整数数组源代码https://mp.csdn.net/mp_blog/creation/editor/124151056 加强版实数数组源代码https://mp.csdn.net/mp_blog/creation/editor/124151110 加强版泛型数组源代码https://mp.csdn.net/mp_bl…

这才是你应该了解的Redis数据结构!

Redis&#xff0c;作为一种高性能的内存数据库&#xff0c;支持多种数据结构&#xff0c;从简单的字符串到复杂的哈希表。在这篇博文中&#xff0c;我们将深入探讨Redis的一些主要数据结构&#xff0c;并通过详细的例子展示它们的使用。 1. 字符串 (String) 1.1 存储和获取 R…

k8s资源介绍

Kubernetes架构图 Kubernetes系统用于管理分布式节点集群中的微服务或容器化应用程序&#xff0c;并且其提供了零停机时间部署、自动回滚、缩放和容器的自愈&#xff08;其中包括自动配置、自动重启、自动复制的高弹性基础设施&#xff0c;以及容器的自动缩放等&#xff09;等…

模糊数学在处理激光雷达的不确定性和模糊性问题中的应用

模糊数学是一种用于处理不确定性和模糊性问题的数学工具&#xff0c;它可以帮助我们更好地处理激光雷达数据中的不确定性和模糊性。激光雷达是一种常用的传感器&#xff0c;用于测量目标物体的距离、速度和方向等信息。然而&#xff0c;在实际应用中&#xff0c;激光雷达所获取…

ITK + ANT,无法显示三维

背景&#xff1a;之前用ANT保存ima格式的数据&#xff0c;选择的是保存所有的序列 用python将dicom转为nii的格式&#xff0c; import nibabel as nib import torch"""不管是nii还是nii.gz都是二维的&#xff0c;为啥呢"""fobj nib.load("…

Linux编辑器---vim

目录 1、vim的基本概念 2正常/普通/命令模式(Normal mode) 2、1命令模式下一些命令&#xff08;不用进入插入模式&#xff09; 3插入模式(Insert mode) 4末行/底行模式(last line mode) 4、1底行模式下的一些命令 5、普通用户无法进行sudo提权的解决方案 6、vim配置问题 6、1配…

使用 Node 创建 Web 服务器

Node.js 提供了 http 模块&#xff0c;http 模块主要用于搭建 HTTP 服务端和客户端&#xff0c;使用 HTTP 服务器或客户端功能必须调用 http 模块&#xff0c;代码如下&#xff1a; var http require(http); 以下是演示一个最基本的 HTTP 服务器架构(使用 8080 端口)&#x…

100天精通鸿蒙从入门到跳槽——第8天:TypeScript 知识储备:泛型

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通Golang》…

学好UEFI,实现从工程师到架构师的跨越

学好UEFI&#xff0c; 实现从工程师到架构师的跨越 2024 / 01 / 19 统一可扩展固件接口 UEFI&#xff0c;是由英特尔、微软等众多全球知名 IT企业共同开发、管理与推进的全新一代 BIOS 体系规范&#xff0c;目前作为最先进最完善的固件架构&#xff0c;已占据绝大部分计算机市…

基于Redisson的RAtomicLong实现全局唯一工单号生成器

最近几年&#xff0c;我一直从事的是运营平台业务开发。每天&#xff0c;我们都需要处理大量的工单配置工作。为了生成工单号&#xff0c;我们建立了一张专用的数据库表&#xff0c;用于记录和生成工单号。每次创建工单时&#xff0c;我们会查询这张表&#xff0c;根据年份字段…

梁山泊国潮风礼盒,传承经典,贺礼新春

在春节来临之际&#xff0c;梁山泊隆重推出新年中国红礼盒酒&#xff0c;为您传递新年的祝福与关爱。这款酒以其独特的魅力&#xff0c;为您带来美好的祝愿和愉悦的享受。中国风国潮礼盒采用中国传统红色为主色调&#xff0c;象征着吉祥、喜庆和繁荣。红色的背景上&#xff0c;…

appium连接手机进行启动失败 ,怎么办 ?检查下这几个地方 。

在使用appium做app自动化&#xff0c;首先需要启动appium连接到手机&#xff0c;然后进行后续操作。 但是往往在启动的时候就会卡住&#xff0c;在点击start session后就会出现报错&#xff0c;具体如下图 &#xff1a; 那么&#xff0c;出现如上的情况该如何解决呢 &#xff1…

(蓝桥杯每日一题)love

问题描述 马上就要到七夕情人节了&#xff0c;小蓝在这天想要心爱得男神表白&#xff0c;于是她写下了一个长度为n仅由小写字母组成的字符串。 她想要使这个字符串有 1314个 love 子序列但是马虎的小蓝却忘记了当前已经有多少个子序列为 love。 请你帮小蓝计算出当前字符串有多…

挑战杯参赛总结-时间序列预测

参赛任务&#xff1a; 目标&#xff1a;针对中国各个市区的不同年份二氧化碳排放量&#xff0c;预测未来年份的二氧化碳排放量。 不同与之前我学习过的波士顿房价预测机器学习-波士顿房价预测-CSDN博客 房价预测是通过学习与房价有关的很多特征&#xff0c;训练出一个模型来预…

RabbitMQ-生产者可靠性

一、生产者重连 1、概念 由于网络波动导致客户端无法连接上MQ&#xff0c;这是可以开启MQ的失败后重连机制。 注意&#xff1a; 是连接失败的重试&#xff0c;而不是消息发送失败后的重试。 2、开启配置 spring:rabbitmq:template:retry:enabled: true # 是否启用重试机制ma…

[python语言]数据类型

目录 知识结构​编辑 复数类型 整数类型、浮点数类型 1、整型 2、浮点型 字符与字符串 1、转义字符 2、字符串的截取 3、字符串的拼接级连 4、字符串的格式化 1、format格式化 2、字符格式化 3、f标志位格式化--(推荐) 5、字符串的常用属性 1、对字符串做出判断…

Mat - 基本映像容器

目标 我们有多种方法可以从现实世界中获取数字图像&#xff1a;数码相机、扫描仪、计算机断层扫描和磁共振成像等等。在每种情况下&#xff0c;我们&#xff08;人类&#xff09;看到的都是图像。但是&#xff0c;当将其转换为我们的数字设备时&#xff0c;我们记录的是图像每…
最新文章