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.mzph.cn/news/634313.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端面试题(持续更新~~)

文章目录 一、基础1、数组常用的方法2、数组有哪几种循环方式&#xff1f;分别有什么作用&#xff1f;3、字符串常用的方法4、原型链5、闭包6、常见的继承7、cookie 、localstorage 、 sessionstrorage区别8、数组去重方法9、http 的请求方式10、数据类型的判断方法11、cookie …

性能压力测试:企业成功的关键要素

性能压力测试在现代企业软件开发中扮演着至关重要的角色&#xff0c;它不仅仅是一项技术手段&#xff0c;更是保障企业成功的关键要素。本文将探讨性能压力测试在企业中的重要性&#xff0c;并阐述其对业务稳定性、用户体验和品牌声誉的影响。 一、保障业务稳定性 1、应对高负载…

使用zabbix-proxy进行分布式监控

目录 一、准备4台服务器 二、配置主从复制 1.准备环境 2.主机名解析 3.安装数据库 4.配置主库db1 5.配置从库db2 6.主从状态显示 三、db1&#xff0c;db2配置zabbix-agent 三、zabbix-server的配置 四、zabbix-proxy的配置 1.为您的平台安装和配置Zabbix-proxy a. …

操作教程|JumpServer堡垒机结合Ansible进行批量系统初始化

运维人员常常需要对资产进行系统初始化的操作&#xff0c;而初始化服务器又是一项繁琐的工作&#xff0c;需要花费运维人员大量的时间和精力。为了提高效率&#xff0c;许多组织会使用自动化工具和脚本来简化这些任务。自动化工具的运用可以大幅降低运维人员的工作量&#xff0…

Redis实战之-分布式锁

一、基本原理和实现方式对比 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程进行&#xff0c;让程序串行…

档案数字化如何选择合适的扫描仪

选择合适的扫描仪是进行档案数字化的关键步骤。以下是一些选择合适扫描仪的要点&#xff1a; 1. 扫描速度&#xff1a;选择具有合适的扫描速度的扫描仪&#xff0c;以便能够快速处理大量的文件。 2. 扫描分辨率&#xff1a;扫描分辨率决定了扫描后图像的清晰度。对于大多数文档…

RIP基础实验配置

要使用RIP完成以上命令需求 1&#xff0c;首先划分ip地址 有图可见有四个网段需要划分 192.168.1.0/26 192.168.3.0/26 192.168.7.0/26 192.168.5.0/26 给两个骨干网段&#xff0c;给两个环回接口&#xff0c;由下图所示&#xff1a; 其次&#xff0c;规划好ip后在各个接口…

v-if和v-for哪个优先级更高?

v-if和v-for哪个优先级更高&#xff1f; 结论&#xff1a; vue2输出的渲染函数是先执行循环&#xff0c;在看条件判断&#xff0c;如果将v-if和v-for写在一个标签内&#xff0c;哪怕只渲染列表中的一小部分&#xff0c;也要重新遍历整个列表&#xff0c;无形造成资源浪费。vu…

DolphinDB 与盈米基金达成战略合作,打造领先的资管机构投顾解决方案

1月16日上午&#xff0c;DolphinDB 与盈米基金在上海签署战略合作协议&#xff0c;共同开启专业资管投顾投研合作新篇章。 DolphinDB 联合创始人、COO 初阳春与盈米基金副总裁、研究院院长杨媛春出席仪式&#xff0c;并代表双方完成签约。 打造市场领先的资管机构投顾服务 盈…

浅析Redis①:命令处理核心源码分析(上)

写在前面 Redis作为我们日常工作中最常使用的缓存数据库&#xff0c;其重要性不言而喻&#xff0c;作为普调开发者&#xff0c;我们在日常开发中使用Redis&#xff0c;主要聚焦于Redis的基层数据结构的命令使用&#xff0c;很少会有人对Redis的内部实现机制进行了解&#xff0c…

vscode设置terminal的最大行数

今天跑代码出现一个问题&#xff0c;就是整个程序跑完&#xff0c;整个程序的输出信息过多&#xff0c;最开始输出的信息已经被vscode的缓存冲掉了&#xff0c;只能看到最后的一部分&#xff0c;具体的原因是vscode的terminal默认只能保存1000行的信息&#xff0c;所以如果想保…

《WebKit 技术内幕》之四(1): 资源加载和网络栈

第四章 资源加载和网络栈 使用网络栈来下载网页和网页资源是渲染引擎工作的第一步 1.WebKit 资源加载机制 1.1 资源 网页本身就是一种资源、网页还需要依赖很多其他的资源(图片、视频) &#xff08;1&#xff09;HTML 支持的资源主要包括以下几种类型&#xff1a; HTML 页…

Elasticsearch 数据类型相关总结:快速参考指南【记录】

在Elasticsearch中&#xff0c;有多种数据类型可用于定义字段。 在开始了解数据类型之前&#xff0c;首先要知道&#xff0c;在Elasticsearch中&#xff0c;分词处理主要针对文本字段&#xff0c;而对于其他类型字段&#xff08;如数值、日期、布尔等&#xff09;&#xff0c;通…

周五的胡思乱想

众所周知 csdn 在程序员的心目中是比较逊色的, 因为博客水平的参差不齐, 大部分人也都是用来作为自己的笔记方便未来复制。这样就导致这里的文章都是点到为止&#xff0c;没有去深究问题的根本原因&#xff0c;大家也都是复制一下解决方案就关闭的页面。或许这就是 csdn 的价值…

视频直播新时代,低延时直播交互,Web,Android,WebRtc推流拉流测试

直播现在已经深入了生活&#xff0c;学习&#xff0c;工作和娱乐方方面面&#xff0c;由于前些年的技术所限&#xff0c;传统rtmp,flv,m3u8 技术让直播快速启动项目产品&#xff0c;但也有很多不足&#xff0c;特别的交互式直播&#xff0c;一直是其中的痛点&#xff0c;延时较…

Oracle学习笔记——基础一起学 14

第十四天 DECODE的简单例子 用case实现 --DECODE的简单例子 --用case实现 select id,name, case sex when 1 then 男 when 2 then 女 end 性别 from student; --DECODE取出一行内两列中的较大值 --先建表 create table sales(month char(2),sales_tv number,sales_…

C#MQTT编程07--MQTT服务器和客户端(wpf版)

1、前言 上篇完成了winform版的mqtt服务器和客户端&#xff0c;实现了订阅和发布&#xff0c;效果666&#xff0c;长这样 这节要做的wpf版&#xff0c;长这样&#xff0c;效果也是帅BBBB帅&#xff0c;wpf技术是cs程序软件的福音。 wpf的基础知识和案例项目可以看我的另一个专…

Parade Series - RTSP - Web

Cron Startup Scripts └─ lt-init.cmd├─ lt-server.cmd│ └─ lt-rtsp-proxy-server.cmd│ └─ lt-rtsp-proxy-agent.cmd└─ lt-push.cmd└─ lt-rtsp-cam-daemon.cmd└─ lt-rtsp-cam-worker.cmdlt-init.cmd :: :: PLEASE DO NOT EDIT THIS FILE :: ECHO OFF…

Qt —— 编译Qt5版本QFTP库,并实现连接服务、获取列表、上传、下载、删除文件等操作(附源码、附基于Qt5编译好的QFTP库)

示例效果1 示例效果2 介绍 QFTP是Qt4的库,Qt5改用了QNetworkAccessManager来代替。但是Qt5提供的QNetworkAccessManager仅支持FTP的上传和下载,所以只能将QFTP库编译为Qt5的库来进行调用。 QFTP在Github的下载地址:https://github.com/qt/qtftp 客户端源码生成的release结果…

IDEA项目启动报错之Command too long

使用IDEA最新的版本2023-3月份社区版本&#xff0c;启动之前没问题的项目突然报错如下&#xff1a; Error running VipServiceApplication: Error running // VipServiceApplication.Command line is too long. Shorten the command line via // JAR manifest or via a // clas…