Linux c/c++服务器开发实践

在Linux C++开发环境中,通常有两种方式来开发多线程程序,一种是利用POSIX多线程 API函数来开发多线程程序,另外一种是利用C++自带线程类来开发程序。

常见的与线程相关的基本API函数:

API函数含义
pthread_create创建线程
pthread_exit线程终止自身执行
pthread_join等待一个线程的结束
pthread_self获取线程ID
pthread_cancel取消另外一个线程
pthread_kill向线程发送一个信号

pthread_join是个阻塞函数,函数pthread_join会让主线程挂起(即休眠,让出CPU),直到子线程都退出,同时pthread_join能让子线程所占的资源得到释放。子线程退出以,主线程会接收到系统的信号,从休眠中恢复。

线程的创建

下面举一个简单的创建线程的例子,新建一个createThreadTest.cpp 文件

例子1. 创建一个简单的线程,不传参数。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>void *thfunc(void *arg){printf("in thfunc\n");return (void*)0;
}int main(int argc, char *argv[]){pthread_t tidp;int ret;ret = pthread_create(&tidp,NULL,thfunc,NULL); //创建线程if(ret){printf("pthread_create failed:%d\n",ret);return -1;}sleep(1); //main线程挂起1秒钟,为了让子线程有机会执行printf("in main:thread is created\n");return 0;
}

在终端执行:

g++ -o createThreadTest createThreadTest.cpp -lpthread
./createThreadTest.cpp 

输出:

in thfunc
in main:thread is created

在这个例子中,首先创建一个线程,线程函数在打印一行字符串后结束,而主线程在创建子线程后会等待一秒钟,避免主线程的过早结束而导致进程结束。如果没有等待函数sleep,则可能子线程的线程函数还没来得及执行,主线程就结束了。

例子2.创建一个线程,并传入整形参数。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>void *thfunc(void *arg){int *pn = (int*)(arg);int n = *pn;printf("in thfunc n = %d\n", n);return (void*)0;
}int main(int argc, char *argv[]){pthread_t tidp;int ret, n = 110;ret = pthread_create(&tidp,NULL,thfunc,&n); //创建线程if(ret){printf("pthread_create failed:%d\n",ret);return -1;}pthread_join(tidp,nullptr);printf("in main:thread is created\n");return 0;
}

终端执行

g++ -o createThreadTest2 createThreadTest2.cpp -lpthread

输出如下:

in thfunc n = 110
in main:thread is created

例2和例1有两点不同,一是创建线程的时候,把一个整形变量的地址作为参数传给线程函数;二是等待子线程结束没有用sleep函数,而用pthread_join。sleep只是等待一个固定的时间,有可能在这个固定的时间内,子线程早已经结束,或者子线程运行的时间大于这个固定时间,因此用它来等待子线程结束并不精确;而用thread_join则会一直等到子线程结束后才会执行该函数后面的代码,我们可以看到它的第一个参数是子线程的ID。

例子3,创建一个线程,并传递字符串作为参数。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>void *thfunc(void *arg){char *str;str = (char *)arg;printf("in thfunc str = %s\n", str);return (void*)0;
}int main(int argc, char *argv[]){pthread_t tidp;int ret;const char *str = "hello world";ret = pthread_create(&tidp,NULL,thfunc,(void *)str); //创建线程if(ret){printf("pthread_create failed:%d\n",ret);return -1;}pthread_join(tidp,nullptr);printf("in main:thread is created\n");return 0;
}
wjr@DESKTOP-UMB8379:~/compuThink$ g++ -o createThreadTest3 createThreadTest3.cpp -lpthread
wjr@DESKTOP-UMB8379:~/compuThink$ ./createThreadTest3
in thfunc str = hello world
in main:thread is created

例4.创建一个线程,并传递结构体作为参数。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>typedef struct{ //定义结构体的类型int n;char *str;
}MYSTRUCT;void *thfunc(void *arg){MYSTRUCT *p = (MYSTRUCT*)arg;printf("in thfunc:n = %d, str = %s\n", p->n,p->str);return (void*)0;
}int main(int argc, char *argv[]){pthread_t tidp;int ret;MYSTRUCT mystruct;mystruct.n = 110;mystruct.str = "hello world";ret = pthread_create(&tidp,NULL,thfunc,(void *)&mystruct); //创建线程if(ret){printf("pthread_create failed:%d\n",ret);return -1;}pthread_join(tidp,nullptr);printf("in main:thread is created\n");return 0;
}
wjr@DESKTOP-UMB8379:~/compuThink$ g++ -o createThreadTest4 createThreadTest4.cpp -lpthread
createThreadTest4.cpp: In function ‘int main(int, char**)’:
createThreadTest4.cpp:21:20: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]21 |     mystruct.str = "hello world";|                    ^~~~~~~~~~~~~
wjr@DESKTOP-UMB8379:~/compuThink$ ./createThreadTest4
in thfunc:n = 110, str = hello world
in main:thread is created

例5.创建一个线程,共享进程数据

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>int gn = 10; //定义一个全局变量,将会在主线程和子线程中用到void *thfunc(void *arg){gn++;   //递增1printf("in thfunc gn = %d\n", gn);return (void*)0;
}int main(int argc, char *argv[]){pthread_t tidp;int ret;ret = pthread_create(&tidp,NULL,thfunc,NULL); //创建线程if(ret){printf("pthread_create failed:%d\n",ret);return -1;}pthread_join(tidp,nullptr);     //等待子线程结束gn++;                           //子线程结束后,gn再递增1printf("in main gn=%d\n",gn);  //再次打印全局变量gn的值return 0;
}
wjr@DESKTOP-UMB8379:~/compuThink$ g++ -o createThreadTest5 createThreadTest5.cpp -lpthread
wjr@DESKTOP-UMB8379:~/compuThink$ ./createThreadTest5
in thfunc gn = 11
in main gn=12

从此例中可以看到,全局变量gn首先在子线程中递增1,等子线程结束后,再在主线程中递增1。两个线程都对同一个全局变量进行了访问。

###线程的属性
POSIX标准规定线程具有多个属性。线程的主要属性包括:分离状态(Detached State)、调度策略和参数(Scheduling Policy and Parameters)、作用域(Scope)、栈尺寸(Stack Size)、栈地址(Stack Address)、优先级(priority)等。Linux为线程属性定义一个联合体pthread_attr_t,注意是联合体而不是结构体,定义的地方在/usr/include/bits/pthreadtypes.h中,定义如下:

union pthread_attr_t
{char __size[__SIZEOF_PTHREAD_ATTR_T];long int __align;
}

从这个定义中可以看出,属性值都是存放在数组_size中的,不方便存取。但在Linux中有一组专门用于存取属性值的函数。如果获取线程的属性,首先要用函数pthread_getattr_np来获取属性结构体值,再用相应的函数来获取某个属性具体值。函数pthread_getattr_np声明如下:

int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr);

其中参数thread是线程ID,attr返回线程属性结构体的内容。如果函数成功,返回0.否则返回错误码。注意,使用该函数需要在pthread.h钱定义宏_GNU_SOURCE,,代码如下

#define _GNU_SOURCE
#include <pthread.h>

并且,当函数pthread_getattr_np获得的属性结构体变量不再需要的时候,应该用函数pthread_attr_destroy进行销毁。
我们前面用pthread_create创建线程的时候,属性结构体指针参数用了NULL,此处创建的线程具有默认属性,即为非分离,大小为1MB的堆栈,与父进程具有同样级别的优先级。如果要创建非默认属性的线程,可以在创建线程之前用函数pthread_attr_init来初始化一个线程属性结构体,再调用相应的API函数来设置相应的属性。接着把属性结构体的指针作为参数传入pthread_create。函数pthread_attr_init声明如下:

int pthread_attr_init(pthread_attr_t *attr);

其中参数attr为指向线程属性结构体的指针。如果函数成功,返回0.否则返回一个错误码。
需要注意的是,使用pthread_attr_init初始化线程属性,使用完(即传入pthread_create)后需要使用pthread_attr_destroy进行销毁,从而释放相应资源。函数pthread_attr_destroy声明如下:

int pthread_attr_destroy(pthread_attr_t *attr);

1.分离状态

分离状态是线程的一个重要属性。POSIX线程的分离状态决定了一个线程以什么样的方式终止。默认的分离状态是可连接,即创建线程时如果使用默认属性,则分离状态属性就是可连接。
POSIX的线程要么是分离状态的,要么是非分离状态的(也称为可连接的,joinable)。前者用宏PTHREAD_CREATE_DETACHED表示,后者用宏PTHREAD_CREATE_JOINABLE表示。

默认情况下创建的线程是可连接的。

一个可连接的线程可以被其他线程收回资源和取消,并且它不会主动释放资源(比如栈空间),必须等待其他线程来回收其资源,因此我们要在主线程使用pthread_join,该函数是个阻塞函数,当它返回时,所等待的线程的资源也释放了。
再次强调,如果是可连接线程,当线程函数自己返回结束时或调用pthread_exit结束时,都不会释放线程所占用的堆栈和线程描述符(总计8K多),必须自己调用pthread_join且返回后,这些资源才会被释放。

这对于父进程长时间运行的线程来说,其结果会是灾难性的。因为父进程不退出并且没有调用pthread_join,则这些可连接线程的资源就一直不会释放,相当于僵尸线程了,僵尸线程越来越多,以后再想创建新线程将会变得没有资源可用。
如果不用pthread_join,即使父进程先于可连接子线程退出,也不会泄露资源。

如果父进程先于子线程退出,那么它将被init进程所收养,这个时候init就是它的父进程,它将调用wait系列函数为其回收资源,因此不会泄露资源。

总之,一个可连接的线程所占用的内容仅当有线程对其执行pthread_join才会释放,因此为了避免内存泄露,可连接的线程在终止时,要么已被设为DETACHED(可分离),要么使用pthread_join来回收其资源。另外,一个线程不能被多个线程等待,否则第一个受到信号的线程成功返回,其中调用pthread_join的线程将得到错误代码ESRCH。

了解了可连接线程,我们来看可分离的线程,这种线程运行结束时,其资源将立即被系统回收。可以这样理解,这种线程能独立(分离)出去,可以自生自灭,父线程不用管它了。将一个线程设置为可分离状态有两个方式,一种是调用函数pthread_detach,它可以将线程转换为可分离线程;另一种是在创建线程的时候将它设置为可分离状态。

基本过程是首先初始化一个线程属性的结构体变量(通过函数pthread_attr_init),然后将其设置为可分离状态(通过函数pthread_attr_setdetachstate),最后将该结构体变量的地址作为参数传入线程创建函数pthread_create,这样创建出来的线程就直接处于可分离状态。

函数pthread_attr_setdetachsate用于设置线程的可分离状态属性,声明如下:

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

例6.创建一个可分离线程。

#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void *thfunc(void *arg){cout << ("sub thread is runnning\n");return NULL;
}int main(int argc, char *argv[]){pthread_t thread_id;pthread_attr_t thread_attr;struct sched_param thread_param;size_t stack_size;int res;res = pthread_attr_init(&thread_attr);if(res)cout << "pthread_attr_init failed:" << res << endl;res = pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);if(res)cout << "pthread_attr_setdetachstate failed:" << res << endl;res = pthread_create(&thread_id, &thread_attr, thfunc, NULL);if(res)cout << "pthread_create failed:" << res << endl;cout << "main thread will exit\n" << endl;sleep(1);return 0;
}
wjr@DESKTOP-UMB8379:~/compuThink$ g++ -o detachTest1 detachTest1.cpp -lpthread
wjr@DESKTOP-UMB8379:~/compuThink$ ./detachTest1 
main thread will exit
sub thread is runnning

在上面代码中,我们首先初始化了一个线程属性结构体,然后设置其分离状态为PTHREAD_CREATE_DETACHED,并用这个属性结构体作为参数传入线程创建函数中。这样,创建出来的线程就是可分离线程。这意味着,该线程结束时,它所占用的任何资源都可以立即让系统回收。程序的最后,我们让主线程挂起一秒,让子线程有机会执行。因为如果主线程很早就退出,将会导致整个进程很早退出,子线程就没有机会执行了。

如果子线程执行的时间长,则sleep的设置比较麻烦。有一种机制不用sleep函数即可让函数完整执行。对于可连接线程,主线程可以用pthread_join函数等待子线程结束。而对于可分离线程,并没有这样的函数,但可以采用这样的方法:
先让主线程退出而进程不退出,一直等待子线程退出了,主线程才退出,即在主线程中调用pthread_exit,在主线程如果调用了pthread_exit,那么终止的只是主线程,而进程的资源会为主线程创建的其他线程保持打开的状态,直到其他线程都终止。 值得注意的是,如果在非主线程(即其他子线程)中调用pthread_exit则不会有这样的效果,只会退出当前子线程。下面不用sleep函数,重新改写例6.

例7,创建一个可分离线程,且主线程先退出

#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void *thfunc(void *arg){cout << ("sub thread is runnning\n");return NULL;
}int main(int argc, char *argv[]){pthread_t thread_id;pthread_attr_t thread_attr;struct sched_param thread_param;size_t stack_size;int res;res = pthread_attr_init(&thread_attr);if(res)cout << "pthread_attr_init failed:" << res << endl;res = pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);if(res)cout << "pthread_attr_setdetachstate failed:" << res << endl;res = pthread_create(&thread_id, &thread_attr, thfunc, NULL);if(res)cout << "pthread_create failed:" << res << endl;cout << "main thread will exit\n" << endl;pthread_exit(NULL); //主线程先退出,但进程不会此刻退出,下面的语句不会执行cout << "main thread has exited, this line will not run\n" << endl;return 0;
}
wjr@DESKTOP-UMB8379:~/compuThink$ g++ -o detachTest2 detachTest2.cpp -lpthread
wjr@DESKTOP-UMB8379:~/compuThink$ ./detachTest2
sub thread is runnning
main thread will exit

例8,获取线程的分离状态属性

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

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

相关文章

从0到1实现一个前端监控系统(附源码)

目录 一、从0开始 二、上报数据方法 三、上报时机 四、性能数据收集上报 收集上报FP 收集上报FCP 收集上报LCP 收集上报DOMContentLoaded 收集上报onload数据 收集上报资源加载时间 收集上报接口请求时间 五、错误数据收集上报 收集上报资源加载错误 收集上报js错…

详解JS中的对象

1、语法 对象的声明有两种形式&#xff0c;声明&#xff08;文字&#xff09;形式和构造形式 // 文字形式 var mtObj {key:value... } // 构造形式 var myObj new Object() myObj.key value构造形式与文字形式生成的对象是一样的&#xff0c;唯一的区别在于文字形式可以添…

Linux下C++调用python脚本实现LDAP协议通过TNLM认证连接到AD服务器

1.前言 首先要实现这个功能&#xff0c;必须先搞懂如何通过C调用python脚本文件最为关键&#xff0c;因为两者的环境不同。本质上是在 c 中启动了一个 python 解释器&#xff0c;由解释器对 python 相关的代码进行执行&#xff0c;执行完毕后释放资源。 2 模块功能 2.1python…

Windows server 2008 R2 IIS搭建ASP网站教程

一、安装应用程序服务器 提示安装成功 二、添加角色服务asp 三、asp网站配置 放入源码 设置网站首页为index.asp: 设置应用程序池 四、设置网站目录属性 五、access数据库连接配置 Cd c:\Windows\System32\inetsrv appcmd list apppool /xml | appcmd set apppool /…

K9203 996920302 面向DNP3的网络安全解决方案

K9203 996920302 面向DNP3的网络安全解决方案 2014年ISA卓越技术创新奖获得者&#xff0c;超电子&#xff0c;3eTI的CyberFence工业防火墙解决方案提供强大加密和应用程序级深度数据包检测(DPI)功能。最近&#xff0c;3eTI为其CyberFence产品线增加了DNP3(分布式网络协议)支持…

替代huggingface下载大模型的站点

0. 引入 huggingface是个好网站&#xff0c;大模型的下载都得靠他&#xff0c;但是网络问题一直是一言难尽&#xff0c;所以只好找找其他替代品。目前发现的代替网站如下&#xff0c;后续如有新发现&#xff0c;还会再来更新到这里。 1. aliendao https://aliendao.cn/ 是一…

SpringBoot Web开发

SpringBoot3-Web开发 SpringBoot的Web开发能力&#xff0c;由SpringMVC提供。 Web开发的三种方式 方式处理过程注意事项实现效果全自动直接编写控制逻辑全部使用自动给配置默认效果手自一体Configuration、 配置WebMvcConfigurer、 配置WebMvcRegistrations不要标注 EnableWeb…

【Linux】:静动态库

静动态库 一.静态库1.设计静态库2.生成静态库3.发布静态库4.使用静态库 二.动态库1.设计动态库2.生成和发布动态库3.使用 一.静态库 程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。 静态库链接格式&#xff1a;libxxx.a(前缀是lib,后缀是…

2023-11-12

今日比较摆烂, 但是把自写管道的原理搞懂了, 主要是把 exp 完完全全看懂了, 还不错. 然后就没干啥了. 明日计划: 学校的作业. AFL 源码. 我真是服了我自己了, AFL 源码搁多久了, 操操操 然后把 seccomp 重新学习下

基于安卓android微信小程序的四六级助手系统

项目介绍 随着我国教育需求不断增加&#xff0c;高校教育资源有限&#xff0c;教育经费相对不足的情况下&#xff0c;利用现代信息技术发展高等教育&#xff0c;不仅充分利用了优秀的教育资源&#xff0c;而且为更多的人提供接受高等教育的机会&#xff0c;同时这也是极大促进…

【Unity每日一记】“调皮的协程”,协程和多线程的区别在哪里

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

网络原理-UDP/TCP详解

一. UDP协议 UDP协议端格式 由上图可以看出&#xff0c;一个UDP报文最大长度就是65535. • 16位长度&#xff0c;表示整个数据报&#xff08;UDP首部UDP数据&#xff09;的最大长度&#xff08;注意&#xff0c;这里的16位UDP长度只是一个标识这个数据报长度的字段&#xff0…

机器视觉目标检测 - opencv 深度学习 计算机竞赛

文章目录 0 前言2 目标检测概念3 目标分类、定位、检测示例4 传统目标检测5 两类目标检测算法5.1 相关研究5.1.1 选择性搜索5.1.2 OverFeat 5.2 基于区域提名的方法5.2.1 R-CNN5.2.2 SPP-net5.2.3 Fast R-CNN 5.3 端到端的方法YOLOSSD 6 人体检测结果7 最后 0 前言 &#x1f5…

单链表按位序与指定结点 删除

按位序删除(带头结点) #define NULL 0 #include<stdlib.h>typedef struct LNode {int data;struct LNode* next; }LNode, * LinkList;//按位序删除&#xff08;带头结点&#xff09; bool ListInsert(LinkList& L, int i, int& e) {if (i < 1)return false;L…

iPhone或在2024开放第三方应用商店。

iPhone或开放第三方应用商店&#xff0c;可以说这是一个老生常谈的话题。对于像是iOS这样封闭的系统来说&#xff0c;此前传出苹果可能开放侧载消息的时候&#xff0c;又有谁能信&#xff0c;谁会信&#xff1f; 如果是按照苹果自身的意愿&#xff0c;这种事情自然是不可能发生…

RestTemplate.postForEntity 方法进行 HTTP POST 请求

RestTemplate 是 Spring Framework 提供的一个用于处理 HTTP 请求的客户端工具。其中&#xff0c;postForEntity 是 RestTemplate 提供的用于发送 HTTP POST 请求并返回 ResponseEntity 对象的方法。 public <T> ResponseEntity<T> postForEntity(String url, Obj…

Antd React Form.Item内部是自定义组件怎么自定义返回值

在线演示https://stackblitz.com/edit/stackblitz-starters-xwtwyz?filesrc%2FSelfTreeSelect.tsx 需求 当我们点击提交,需要返回用户名和选中树的id信息,但是,我不关要返回树的id信息,还需要返回选中树的名称 //默认返回的 {userName:梦洁,treeInfo:leaf1-value } //但是需…

SpringBoot项目调用openCV报错:nested exception is java.lang.UnsatisfiedLinkError

今天在通过web项目调用openCV的时候提示如下错误&#xff1a; nested exception is java.lang.UnsatisfiedLinkError:org.opencv.imgcodecs.Imgcodecs.imread_0(Ljava/la如下图所示&#xff1a; 但是通过直接启动java main函数确正常&#xff0c;初步诊断和SpringBoot热加载…

55基于matlab的1.高斯噪声2.瑞利噪声3.伽马噪声4.均匀分布噪声5.脉冲(椒盐)噪声

基于matlab的1.高斯噪声2.瑞利噪声3.伽马噪声4.均匀分布噪声5.脉冲&#xff08;椒盐&#xff09;噪声五组噪声模型&#xff0c;程序已调通&#xff0c;可直接运行。 55高斯噪声、瑞利噪声 (xiaohongshu.com)

链表的实现(文末附完整代码)

链表的概念及结构 链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的 我们在上一篇文章所学习的顺序表是连续存储的 例如&#xff1a; 顺序表就好比火车上的一排座位&#xff0c;是连续的 而链表就好比是火车…