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

Linux——线程控制

目录

前言

一、线程创建

1.创建线程

2.线程传递结构体 

3.创建多线程 

4.收到信号的线程

二、线程终止

三、线程等待

四、线程分离

五、取消线程

六、线程库管理的原理

七、站在语言角度理解pthread库

八、线程的局部存储


前言

前面我们学习了线程概念和线程创建,今天我们学习线程控制,如何操控一个线程完成任务,同时能取消线程、等待线程,分离线程。

一、线程创建

1.创建线程

功能:创建一个新的线程

  • 参数 thread:返回线程ID
  • attr:设置线程的属性,attr为NULL表示使用默认属性
  • start_routine:是个函数地址,线程启动后要执行的函数
  • arg:传给线程启动函数的参数
  • 返回值:成功返回0;失败返回错误码
#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;void* TreadToutine(void *arg)
{const char* threadname = (const char*) arg;while(1){cout<<"我是一个新线程"<<threadname<<endl;sleep(1);}
}int main()
{pthread_t tid;pthread_create(&tid,NULL,TreadToutine, (void*)"thread 1");//主线程while(1){cout<<"我是主线程"<<endl;sleep(1);}return 0;
}

linux没有真正的线程概念,他的线程是复用的进程代码,只是做了一些区分。线程客观的可以叫做轻量级进程。因此Linux只会提供轻量级进程创建的函数调用,不会直接提供线程创建的接口。因此我们使用pthread原生线程库,编译时需要手动链接库文件(-lpthread)。

这样编译后就可以运行了。

从上面代码可以看出,给线程传递的信息可以是char*,由于pthread_create函数的最后一个参数为void*,同时线程去运行的函数参数也是void*,因此我们任意类型都可以传递过去,进行一下强转即可。

2.线程传递结构体 

比如现在我想传递很多内容过去,叫线程帮我们处理 

如下,我们传递了结构体 

线程成功收到结构体,并做出了处理。

3.创建多线程 

 创建多线程也很简单,只需要循环创建即可。

4.收到信号的线程

如果进程创建的线程有一个发生了异常,收到了信号,会导致整个进程都被终止,因为线程是进程创建出来的,发送信号是发给了进程,进程如果退出,那么该进程所有的资源也都得被回收。而线程本身就是进程资源的一部分。

二、线程终止

我们知道线程去执行的函数返回类型为void*,当线程执行结束,return时,线程就自动终止了

如果我们返回时调用exit()函数 ,那么整个进程都会被终止

同时,pthread.h库还给我们提供了 pthread_exit() 接口,我们使用该接口也可以终止线程。

pthread_exit()

作用:终止一个运行的线程

参数retval:返回void*的全局变量

注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

代码如下,两种方法都可以退出 

 运行结果如下,线程被退出,不再打印消息。

三、线程等待

线程退出默认要被等待,如果不等待,就会发生类似于僵尸进程的问题。因此我们需要用pthread_join()函数进行等待

pthread_join()

功能:等待线程结束

  • 参数1::thread:线程ID
  • 参数2:value_ptr:它指向一个指针,后者指向线程的返回值(void**指向的线程返回值void*)
  • 返回值:成功返回0;失败返回错误码

代码如下,让子线程程循环5次后退出并传参常量字符串,主线程去join等待,并将等待的结果输出。

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <cstdio>
using namespace std;class Add
{
public:Add(string name, int a, int b): _name(name), _a(a), _b(b){}public:string _name;int _a;int _b;
};void *TreadToutine(void *arg)
{Add *a1 = (Add *)arg;int cnt = 5;while (cnt--){cout << "我是一个新线程: " << a1->_name << ",计算结果为" << a1->_a + a1->_b << endl;sleep(1);}//return nullptr;pthread_exit((void*)"pthread-1 退出"); //常量区
}int main()
{pthread_t tid;Add *td = new Add("thread-1", 10, 20);pthread_create(&tid, NULL, TreadToutine, td);// 主线程cout << "我是主线程,子线程的tid: "<< tid << endl;void* msg = nullptr;pthread_join(tid,&msg);cout<<"等待成功,子线程退出信息: "<<(char*)msg <<endl;sleep(1);return 0;
}

等待成功,同时输出了消息。注意等待是阻塞式等待,子线程退出后才会执行后续代码。

四、线程分离

我们知道,线程是需要被等待的,不然会发生类似于僵尸进程的现象,那么如果我想让线程一直去运行,比如说一直帮我播放音乐,那么主线程就会一直等待,不可能执行后面的代码。

在这种情况下,我们可以让线程分离,也就是主线程不再关心创建的子线程的死活,他要运行就运行,不运行了操作系统会回收。不过一般建议主线程最后再退出

可以使用pthread_detach()函数进行线程的分离。

pthread_detach()

作用:分离线程

分离线程很简单,直接调用pthread_detach()就可以,我们不过多展示,下面代码是先分离线程,再等待线程看看会发生什么。 

发现等待线程的返回值为22,不是0证明等待失败,22的意思是该线程不需要等待。

这是我们是在主线程进行分离的,子线程也可以被分离, 由于子线程默认看不到自己的tid,因此可以调用pthread_self()函数获取自己的tid。

pthread_self

作用:让线程获取自己的tid

如下是子线程选择分离。 

小总结:

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。 

五、取消线程

主线程可以取消线程,也就是让子线程退出,可以调用pthread_cancel()函数进行终止线程。

pthread_cancel()

功能:向线程发送取消请求

  • 参数1:thread,线程ID
  • 返回值:成功返回0;失败返回错误码

代码如下,先取消进程,再等待线程,同时查看线程退出码

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <cstdio>
using namespace std;void *TreadToutine(void *arg)
{while(1){cout << "我是一个新线程" << endl;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, NULL, TreadToutine, (void *)"pthread-1");sleep(3);// 取消线程int n = pthread_cancel(tid);cout << "线程取消成功,n: " << n << endl;// 等待线程void *ret = nullptr;n = pthread_join(tid, &ret);cout << "等待线程返回值n: " << n << ",线程返回值: " << (int64_t)ret << endl;return 0;
}

运行看到,线程返回值为0,取消成功,等待返回值为0,等待成功。我们看到线程没有阻塞在等待函数这里,而是直接往后运行,同时进程返回为-1。

这是因为如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数 PTHREAD_ CANCELED

而如果线程先被脱离,再取消,结果怎么样呢?

发现也是能被取消的,但是线程等待是22(等待失败)。因为系统直接回收了。 

小总结:

1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。

2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_CANCELED。

3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。

4. 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数。 

六、线程库管理的原理

我们对线程的操作一直要使用tid,那么tid里面的内容到底是什么呢?

其实他是一个地址,我们转成16进程来看一下。

确实是很像是地址,但这跟LWP(Light Weight Process)也不一样啊。该如何理解呢? 

  • 首先,我们知道pthread. h不是操作系统的接口,而是原生线程库。那么用户创建的线程,操作系统无法管理,则需要线程库来进行管理。他从系统中获取轻量级进程相关属性,从用户中也获取一些属性,这样就先描述起来了,再通过数据结构将线程组织起来,就将线程管理好了。
  • 我们也知道,线程要有独立属性,独立的主要有硬件上下文和栈空间,其中硬件上下文跟操作系统有关,而栈空间则是要从用户中来。栈不是只有一个吗?为什么每一个线程都有自己的栈空间呢?这其实是操作系统帮我们处理了的,操作系统会在堆区创建空间,来充当线程独立的栈。pthread库会获取到栈空间,并将他管理维护好,而默认地址空间中的栈,由主线程使用。

那么线程库如何管理呢,在哪管理呢? 

  • 在进程地址空间中,mmap(共享区)加载了动态库,其中我们使用的pthread库就在该区域,他会管理好每一份线程,每一份线程都在其中有自己的属性集。
  • struct pthread里存在很多线程属性,线程局部存储,还有线程栈,这个栈指向的是堆空间的区域,每当有新线程被创建,都会在后面继续创建这种数据结构。就这样将多个线程统一的描述组织起来了,可以进行管理了。因此我们调用pthread相关函数,相当于对该空间进行访问、处理。

那么现在,我们也可以理解 pthread_t tid 是什么了,他不就是每一个线程在进程地址空间的起始地址嘛,我们pthread_create 对tid进行写入,因为需要创建对应的数据结构,找到起始地址,然后返回,后续用户要继续对线程进行控制,等待啊,终止啊,分离啊,取消啊。都需要传入tid,也就是能找到在进程地址空间的位置后,才可以处理。

七、站在语言角度理解pthread库

我们之前学的pthread库,是Linux提供的原生线程库,在语言层面,比如C++/JAVA\PYTHON,他们也会提供给我们线程库。

我们写了一份代码,使用的是C++提供的线程库 thread

 #include<iostream>#include<unistd.h>#include<thread>using namespace std;void myrun()
{while(1){cout<<"我是一个新线程"<<endl;sleep(1);}
}int main(){thread t(myrun);t.join();}

编译后运行,发现说多线程操作被禁止了,这是因为我们没有链接pthread库。

c++提供的线程库封装了pthread.h。因此我们编译时仍然需要链接 pthread库。

到现在,我们可以知道,语言上也许线程库的使用不一定相同,但是他们底层都是用的linux原生线程库。

在Linux下做了封装,那么这段代码我们可以在Linux中运行。

如果thread头文件在Windows下,封装了Windows线程的操作,那么也可以在Windows下运行。这大大提高了文件的可移植性。

八、线程的局部存储

我们定义一个全局变量,创建线程,让新线程对全局变量做++,观察新线程和主线程全局变量是否发生变化。

#include <iostream>
#include <unistd.h>
#include <pthread.h>
using namespace std;int g_val = 100;void *TreadToutine(void *arg)
{while (1){cout << "我是一个新线程,g_val: " << g_val << ",&g_val: " << &g_val << endl;g_val++;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, NULL, TreadToutine, (void *)"Thread 1");while (1){cout << "我是一个主线程,g_val: " << g_val << ",&g_val: " << &g_val << endl;sleep(1);}pthread_join(tid,nullptr);
}

 我们可以看到,全局变量值一样,地址也一样,我们现在知道全局变量是被所有进程共享的。

如果我们给全局变量前添加上__thread,GCC/G++编译器提供的一个扩展,用于声明线程局部存储变量。

现在运行,主线程和新线程g_val不一样,地址也不一样。

因为我们添加的__thread 会在G++编译时,给每个线程的局部存储空间里将变量拷贝进程,私有一份,于是每个线程自己管理自己的那一份资源。不再与外部共享。 

 只是__thread只能修饰内置类型,如string这种自定义类型无法处理。

相关文章:

Linux——线程控制

目录 前言 一、线程创建 1.创建线程 2.线程传递结构体 3.创建多线程 4.收到信号的线程 二、线程终止 三、线程等待 四、线程分离 五、取消线程 六、线程库管理的原理 七、站在语言角度理解pthread库 八、线程的局部存储 前言 前面我们学习了线程概念和线程创建&…...

【Leetcode 347】,前k个高频元素,小根堆的调整

参考题解 题目&#xff1a;给定一个数组&#xff0c;输出 前k个高频元素。 思路&#xff1a; 遍历数组&#xff0c;建立小根堆&#xff08;小根堆的元素是元组&#xff08;num,freq&#xff09;&#xff0c;排序规则是每个元素的频率&#xff09;。 下面使用数组‘heap’&…...

【图论】【分类讨论】LeetCode3017按距离统计房屋对数目

本文涉及的知识点 图论 分类讨论 本题同解 【差分数组】【图论】【分类讨论】【整除以2】3017按距离统计房屋对数目 LeetCode3017按距离统计房屋对数目 给你三个 正整数 n 、x 和 y 。 在城市中&#xff0c;存在编号从 1 到 n 的房屋&#xff0c;由 n 条街道相连。对所有 …...

浅谈Yum 安装和 源码安装

浅谈Yum 安装和 源码安装 本文所叙述的Linux系统是基于RedHat发行版的CentOS7 yum安装 1. 前言 我们知道在Windows上下载的安装包后缀是 .exe &#xff0c;与之对应的 在 Linux下的安装包的后缀是 .rpm rpm (Red Hat Package Manager) 是红帽软件包管理器 我们在Windows电脑…...

JavaEE初阶Day 3:多线程(1)

目录 Day 3&#xff1a;多线程&#xff08;1&#xff09;1. 线程1.1 引入线程的原因1.2 线程的定义1.3 为何线程更轻量1.4 问题 2. 多线程代码2.1 继承Thread重写run2.2 通过实现Runnable接口创建线程2.3 针对2.1的变形使用匿名内部类2.4 针对Runnable创建匿名内部类2.5 使用la…...

gutil140.dll是什么?gutil140.dll无法继续执行的解决方法

gutil140.dll文件是一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;通常与Microsoft Visual Studio 2015相关联。 gutil140.dll是开发过程中使用的工具函数集合&#xff0c;它辅助开发人员执行常见的编程任务&#xff0c;如文件操作、内存分配和字符串处理等。这个…...

在CentOS 7上安装Python 3.7.7

文章目录 一、实战步骤1. 安装编译工具2. 下载Python 3.7.7安装包3. 上传Python 3.7.7安装包4. 解压缩安装包5. 切换目录并编译安装6. 配置Python环境变量7. 使配置生效8. 验证安装是否成功 二、实战总结 一、实战步骤 1. 安装编译工具 在终端中执行以下命令 yum -y groupin…...

基于SpringBoot Vue宠物领养系统

一、&#x1f4dd;功能介绍 基于SpringBoot Vue宠物领养系统 角色&#xff1a;管理员、用户 当游客打开系统的网址后&#xff0c;首先看到的就是首页界面。在这里&#xff0c;游客能够看到宠物领养救助平台的导航条显示首页、宠物招领、宠物认领、 宠物论坛、宠物资讯、后台管…...

ip命令

ip a 也是ip addr简写 [rootlocalhost ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft…...

【Kaggle】练习赛《鲍鱼年龄预测》(上)

前言 上一篇文章&#xff0c;讲解了《肥胖风险的多类别预测》机器学习方面的文章&#xff0c;主要是多分类算法的运用&#xff0c;本文是一个回归的算法&#xff0c;本期是2024年4月份的题目《Regression with an Abalone Dataset》即《鲍鱼年龄预测》&#xff0c;在此分享高手…...

Ruby 之交租阶段信息生成

题目 我看了一下&#xff0c;这个题目应该不是什么机密&#xff0c;所以先放上来了。大概意思是根据合同信息生成交租阶段信息。 解答 要求是要使用 Ruby 生成交租阶段信息&#xff0c;由于时间比较仓促&#xff0c;变量名那些就用得随意了些。要点主要有下面这些&#xff1a…...

RUST语言值所有权之内存复制与移动

1.RUST中每个值都有一个所有者,每次只能有一个所有者 String::from函数会为字符串hello分配一块内存 内存示例如下: 在内存分配前调用s1正常输出 在分配s1给s2后调用报错 因为s1分配给s2后,s1的指向自动失效 s1被move到s2 s1自动释放 字符串克隆使用...

【Django学习笔记(三)】BootStrap介绍

BootStrap介绍 前言正文1、BootStrap 快速了解2、初识BootStrap2.1 下载地址2.2 创建目录2.3 引入BootStrap2.4 使用BootStrap 3、BootStrap 组件&样式3.1 导航条3.2 栅格系统3.3 container3.3.1 container3.3.2 container-fluid 3.4 面板3.5 媒体对象3.6 分页3.7 图标3.7.…...

ClickHouse开发相关(UDAF)

ClickHouse开发相关(UDAF) ClickHouse介绍 ClickHouse是一个开源、高性能的列式 OLAP 数据库管理系统,用于使用 SQL 进行实时分析。 为什么需要ClickHouse UDAF? ClickHouse中已存在了许多聚合函数,绝大多数情况下已经覆盖我们的需求,但是有时候我们仍然需要自定义函数…...

MySql并发事务问题

事务 事务概念&#xff1a; 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 事务的特性&#xff1a;ACID&#xff1a; 小…...

Windows下Docker创建Mysql5.7

安装 下载镜像&#xff0c;注意&#xff0c;要带版本号 docker pull mysql:5.7 等下载完成执行命令&#xff1a; 错误命令1&#xff0c;直接Windows下路径&#xff1a; docker run --name mysql57 --restartalways -p 3306:3306 -v F:/mysqldata/data57/log:/var/log/mysql…...

Redis(性能管理、主从复制、哨兵模式)概述及部署

目录 一、性能管理 1、查看Redis内存使用 2、内存碎片率 3、跟踪内存碎片率 4、内存使用率 5、内回收key 二、Redis集群有三种模式 三、Redis主从复制 1、主从复制的概念 2、主从复制的作用 3、主从复制的流程 4、搭建Redis主从复制 1.环境准备 2.安装Redis&#…...

LabVIEW挖坑指南

一、挖坑指南 1.1、输出变量放在条件框内 错误写法&#xff1a; 现象&#xff1a;如果没进入对应的分支&#xff0c;输出为默认值 正常写法&#xff1a; 让每个分支输出的值都在预料之内。 1.2、统计耗时不准 错误写法 现象&#xff1a;统计出来的耗时是2000ms 正常写法&a…...

docker容器环境安装记录(MAC M1)(完善中)

0、背景 在MAC M1中搭建商城项目环境时&#xff0c;采用docker统一管理开发工具&#xff0c;期间碰到了许多环境安装问题&#xff0c;做个总结。 1、安装redis 在宿主机新建redis.conf文件运行创建容器命令&#xff0c;进行容器创建、端口映射、文件挂载、以指定配置文件启动…...

Linux 常用命令(持续更新中...)

1. ls 查看文件列表命令 语法&#xff1a; ls [-a -l -h] [Linux路径] -a -l -h 是可选的选项 &#xff08;-h需配合-l命令一起使用&#xff09;Linux路径是此命令可选的参数 ls #查看当前目录所有非隐藏文件(平铺方式显示) ls -a #查看当前目录下所有文件 …...

xss.pwnfunction-Jefff

在eval中可以直接执行命令所以直接把"直接闭合在结尾再加上一个"因为后面的"没闭和会报错 ?jeffa";alert(1);" 或 ?jeffa"-alert(1)-" -是分隔符...

java——文件上传

一、文件上传——简介 文件上传的简介&#xff1a;文件上传是指将本地计算机中的文件传输到网络上的服务器或另一台计算机上的过程。在 Web 开发中&#xff0c;文件上传通常指的是将用户通过 Web 页面提交的文件&#xff08;如图像、文档、音频、视频等&#xff09;传输到服务器…...

RCE(远程命令执行)漏洞详解

漏洞描述 RCE(remote command/code execute&#xff0c;远程命令执行)漏洞 远程代码执行 (RCE) 攻击是指攻击者可以在一个组织的计算机或网络上运行恶意代码。执行攻击者控制的代码的能力可用于各种目的&#xff0c;包括部署额外的恶意软件或窃取敏感数据。 漏洞原理 远程代…...

K8S - Deployment 的版本回滚

当前状态 先看deployment rootk8s-master:~# kubectl get deploy -o wide --show-labels NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES …...

53 v-bind 和 v-model 的实现和区别

前言 这个主要的来源是 偶尔的情况下 出现的问题 就比如是 el-select 中选择组件之后, 视图不回显, 然后 model 不更新等等 这个 其实就是 vue 中 视图 -> 模型 的数据同步, 我们通常意义上的处理一般是通过 模型 -> 数据 的数据同步, 比如 我们代码里面更新了 model.…...

VMware-16.0配置虚拟机网络模式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、为什么要配置网络&#xff1f;二、配置步骤1.检查VMware服务2.进入配置页面3.添加网络模式1.Bridge2.NAT3.Host-only 4.DHCP租约5.静态IP 三、使用总结 前言…...

element-ui badge 组件源码分享

今日简单分享 badge 组件的源码实现&#xff0c;主要从以下两个方面&#xff1a; 1、badge 组件页面结构 2、badge 组件属性 一、badge 组件页面结构 二、badge 组件属性 补充几个标签的用途&#xff1a; sub&#xff1a;下标、sup&#xff1a;上标、var 变量 代码如下&am…...

MySQL中日期有关函数

本次记录了获取日期时间的多种方式&#xff0c;还有日期和字符串之间的转换&#xff0c;以及加减日期的操作。 获取时间 # 1.获取当前时间(年月日时分秒格式) select now();# 2.获取当前时间(年月日格式) select curdate();# 3.获取当前时间(时分秒格式) select curtime();# 4.…...

jdbc工具类

jdbc 工具类&#xff0c;具体见下面代码&#xff0c;直接可以用。 /*** version 1.0* descpription: jdbc工具类* date 2024/4/6*/ public class JDBCUtils {private static final String URL "jdbc:mysql://127.0.0.1:3306/mybatis";private static final String …...

Svelte Web 框架介绍

Svelte 是一个用于构建网络应用程序的现代框架&#xff0c;它与其他用户界面框架&#xff08;如React和Vue&#xff09;有着本质的不同。Svelte 的核心理念是在构建应用程序时&#xff0c;将大部分工作转移到编译步骤中&#xff0c;而不是在用户的浏览器中运行时处理。这种方法…...

杭州网站设计的公司/百度推广最简单方法

背景 通过添加下列类&#xff0c;可以快捷的变换背景颜色&#xff0c;如果是链接的话&#xff0c;鼠标移动上去会变暗 bg-primary 被修饰元素将会应到primary类&#xff0c;显示吃淡蓝色&#xff0c;文本颜色会变成白色。bg-success 被修饰元素表示成功的信息&#xff0c;背景变…...

贷款网站模板/西安百度推广客服电话多少

JAVA中字符串函数subString的用法小结 作者&#xff1a; 字体&#xff1a;[增加 减小] 类型&#xff1a;转载 时间&#xff1a;2014-02-11 我要评论 本篇文章主要是对JAVA中字符串函数subString的用法进行了详细的介绍&#xff0c;需要的朋友可以过来参考下&#xff0c;希望对大…...

室内设计师的网站/网络销售网站

反向代理简介----服务器的代理代理服务器是使用非常普遍的一种将局域网主机联入互联网的一种方式&#xff0c;使用代理上网可以节约紧缺的IP地址资源&#xff0c;而且可以阻断外部主机对内部主机的访问&#xff0c;使 内部网主机免受外部网主机的***。但是&#xff0c;如果想让…...

吉林住房和城乡建设部网站/百度小说风云榜总榜

其实对于字母而言&#xff0c;思路相当于是26个字母的映射表 算法实现如下&#xff1a; // isBroStr2.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> using namespace std; const int MAX58; int isBroStr(char* s1,cha…...

宽带办理需要多少钱/seo咨询茂名

前面发布了很多种二维的曲线图形,其实所有的二维图形再加一个维度值就可以变成三维图形.那么这一节的内容是将一个二维曲线帖到一个球面上,以生成三维曲线. 帖到球上的方式是以如下公式生成第三个维度的数值: y sqrt(a*a - x*x - z*z) 这里举个例子是之前发的一种二维曲线: 数…...

东莞外贸企业做网站/微信营销怎么做

1、下载安装Geoserver 主页地址&#xff1a;http://geoserver.org/ 下载地址 https://jaist.dl.sourceforge.net/project/geoserver/GeoServer/2.15.1/geoserver-2.15.1.exe 2、直接安装 选择jdk的安装位置 C:\Program Files\Java\jdk1.8.0_181 2、最后&#xff0c;在浏览器…...