【Linux】线程Thread

🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于知识分享,与更多的人进行学习交流

线程概述

线程是进程中的一个执行单元

经典的并发模型,多进程模型,占用大量的内存开销,庞大的进程间调度开销,提供一种开销更小,更轻量级的并发解决方案,多线程技术。

线程在进程中,与进程共享资源和数据,进程是容器,线程是执行单元,进程退出可能导致所有线程退出。

每个进程中都有一个主控线程,还有其他的普通线程,操作系统会给进程分配需要的内存单元。

不支持线程技术的操作系统可以忽略线程概念,进程是调度单位。

多线程操作系统下,进程是内存管理单位,线程才是唯一的调度单元。

线程的CPU分配

Linux操作系统下,线程就是轻量级进程,每个进程分配一个LWP

在Linux操作系统下,所有的调度单位都是进程,淡化线程概念

线程的实现方式

用户线程(User Thread):在用户空间实现的线程,不是由内核管理的线程,是由用户态的线程库来完成线程的管理。可以利用第三方库的形式在不支持线程技术的系统下安装和使用线程,用户级线程的调度开销更小,因为大部分的操作都在用户层完成,无需内核干预

内核线程(Kernel Thread):操作系统中每创建一个线程,系统都会为其创建一个内核对象,此线程系统可以识别支持,会为线程执行资源(时间片)。Control控制器会为每个内核级线程创建内核对象,每个内核级线程都会由CPU进行时间片切换

轻量级进程(LightWeight Process):在内核中来支持用户线程;

进程的蜕化

进程内存资源独占,不与其他人共享,进程是独立的调度单位(无需考虑线程问题)

进程中出现了多个执行单元,讨论考虑线程问题

讨论和分析的蜕化,讨论线程,进程的讨论变为主线程和普通线程的分析和讨论

线程间的共享资源和非共享资源

PCB:共享资源

栈:线程栈空间非共享,每个线程创建后,会分配8MB线程栈

库:库资源共享

堆:共享堆空间

全局资源和静态资源:全局资源共享

代码段:代码段共享

文件描述符表:文件描述符表共享

信号的处理行为(某个线程改变信号行为)对所有线程共享。信号屏蔽字非共享,普通线程拥有独立的屏蔽字,拷贝继承于主线程

TCB:每个线程拥有自己的线程控制块,独立的tid

线程开发相关API接口

NPTL线程库

NPTL线程库是典型的内核级线程库,创建的线程可以被系统标识分配cpu资源(轻量级进程)

native Posix thread library

相关命令

ps aux 进程查看

ps ajx 进程关系查看

ps -eLf 所有线程查看 PID相同的是一组线程

LWPPID 相同的情况表示该进程只有一个线程。

ps -Lf pid 查看某一个进程下的线程

每个线程都会被系统分配lwp 调度编号, 便于系统管理调度线程,但是线程拥有独立的tid,线程id

头文件和函数

#include <pthread.h>使用线程函数的头文件

pthread_t tid线程tid类型

pthread_create

创建线程并指定类型pthread_create(pthread_t *tid,NULL,void * (thread_job)(void *),void *arg)第一个参数是线程id,第二个是线程属性,第三个参数是线程工作地址,第四个参数是线程工作参数。
返回值是err

下面是使用pthread_create创建线程的简单demo程序,通过PIDLWP相同可以看出,这个线程就是主控线程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{printf("普通线程被创建,参数为%d\n",*(int*)arg);return NULL;
}int main()
{pthread_t tid;int code=1024;//普通线程都是由主控线程创建的pthread_create(&tid,NULL,thread_job,(void*)&code);printf("普通线程被主控线程创建出来\n");while(1) sleep(1);return 0;
}

在编译时需要链接库

gcc Pthrad_Create.c -lpthread -o app

关于线程函数的错误处理

线程函数出错,会返回错误号(err>0),使用char *errstr=strerror(err)进行错误判断

下面是打印错误日志的demo程序:


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程被创建,参数为%d\n",*(int*)arg);while(1) sleep(1);return NULL;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;//普通线程都是由主控线程创建的while(1){if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}else{printf("线程%d成功创建\n",++flags);}}return 0;
}

32位Ubuntu操作系统下,一个进程只能创建381个线程

pthread_self

pthread_tid =pthread_self(void) 成功返回当前线程的id

主线程通过pthread_create创建普通线程,成功传出tid与普通线程自身利用pthread_self()得到的tid值相等但是进程状态不相等,因为pthread_self()获取tid时可以保证当前的有效性,主线程在获取tid的时候,普通线程可能已经退出。


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());return NULL;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);while(1) sleep(1);//主控线程还在运行,普通线程已经退出return 0;
}

pthread_join(pthread_t tid,void **retval)

线程回收函数,可以回收线程资源的同时获取线程的返回值。经典的阻塞回收函数,会一致等待普通线程结束后,进行回收。


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());return (void*)126;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);pthread_join(tid,&revtal);printf("Thread Exit Code:%ld\n",(long int)revtal);return 0;
}

可以看到,如果线程工作函数可以正常退出,退出码就是线程工作函数的返回值

pthread_cancel(pthread_t tid)

指定线程tid,取消结束线程

下面是简单的取消一个线程的demo程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){printf("进程正在工作。。。\n");sleep(1);}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause();return 0;
}

线程被成功结束,这个进程下只有一个主控线程在工作

当我们对代码稍作修改:

删除线程工作函数中的调用系统函数:

void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause();      return 0;
}

结果显示,pthread_cancel并没有成功取消线程。

这有些类似于信号,信号处理的三个切换条件:系统调用,软件中断,软件异常。而pthread_cancel取消事件的处理条件,必须有系统调用。

有一个专门为pthread_cancel提供系统调用的空函数:void pthread_testcancel(void),提供一次空的调用。

在线程工作函数加入这个函数再次尝试:

void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){pthread_testcancel(void);}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause();      return 0;
}

可以看到只要一个主控线程,普通线程被结束了。

当我们对线程进行取消后,回收一下资源打印退出码看一下:

void * thread_job(void *arg)//普通线程的创建
{//printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());while(1){pthread_testcancel(void);}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pthread_join(tid,&revtal);printf("Thread Exit Code:%ld\n",(long int)revtal);pause();      return 0;
}

可以看到退出码是-1,所以线程指定退出码时不允许使用-1,保留给pthread_cancel

pthread_detach(pthread_t tid)

用于将一个线程设置为分离态线程。

可以通过线程属性,批量创建分离态线程,这些线程诞生即是分离态。

线程有回收态和分离态,这两种状态是互斥的。

若修改线程退出状态,从回收态改为分离态,此操作不可逆,不能将分离线程变为回收态

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>void * thread_job(void *arg)//普通线程的创建
{pthread_detach(pthread_self());while(1){pthread_testcancel();}return (void*)129;
}int main()
{pthread_t tid;int code=1024;int flags=0;int err;void *revtal=NULL;//普通线程都是由主控线程创建的if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0){printf("thread create failed:%s\n",strerror(err));exit(0);//创建失败,进程退出}printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);if((err=pthread_join(tid,&revtal))>0){printf("join call failed %s\n",strerror(err));}printf("Thread Exit Code:%ld\n",(long int)revtal);pause();return 0;
}

joindetach会竞争时间片,所以有时线程以detach分离态被创建,当线程处于分离态时,无法手动回收,join执行失败

如果对一个分离态线程进行回收操作,pthread_join会失效返回。对一个已经处于回收阶段(join已经开始阻塞等待了)的线程设置分离,分离设置失败。

一个自行回收,一个由系统回收,一个可以得到线程退出码,一个无法获取

pthread_exit(void * exitcode)

线程退出函数,结束当前线程,与进程无关,退出码可以被join获取

线程的退出方式

return 0

主线程结束进程;结束普通线程,返回结果

pthread_cancel

主线程结束,与进程无关

普通进程结束,与进程无关

使用普通线程对主线程进行回收,产生了僵尸级线程

pthread_exit()

主线程结束,与进程无关

普通进程结束,与进程无关

exit()

进程退出,释放所有线程

线程属性

在使用创建线程函数时指定线程类型pthread_create(pthread_t *tid,pthread_attr_t *attr,void * (thread_job)(void *),void *arg)

用第二个参数指定线程属性

pthread_attr_t线程属性类型,是一个结构体类型

修改线程程属性的操作

使用pthread_attr_getdetachstate查看线程默认退出属性

使用pthread_attr_getstack查看栈属性


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_getdetachstate(&attr,&State);if(State==PTHREAD_CREATE_JOINABLE)printf("Thread Default State is Join\n");elseprintf("Thread Default State is Detach\n");pthread_attr_getstack(&attr,&stack_addr,&stacksize);printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);return 0;
}

使用pthread_attr_setdetachstate修改线程默认退出状态为分离态

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>void *thread_job(void * arg)
{}int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_getdetachstate(&attr,&State);if(State==PTHREAD_CREATE_JOINABLE)printf("Thread Default State is Join\n");elseprintf("Thread Default State is Detach\n");pthread_attr_getstack(&attr,&stack_addr,&stacksize);printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);pthread_t tid;pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);pthread_create(&tid,&attr,thread_job,NULL);int err=pthread_join(tid,NULL);if(err>0)printf("Join Call Failed:%s\n",strerror(err));return 0;
}

使用pthread_attr_setstacksize修改默认线程栈大小

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>void *thread_job(void * arg)
{}int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_getdetachstate(&attr,&State);if(State==PTHREAD_CREATE_JOINABLE)printf("Thread Default State is Join\n");elseprintf("Thread Default State is Detach\n");pthread_attr_getstack(&attr,&stack_addr,&stacksize);printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);pthread_t tid;pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);stacksize=0x100000;int err;int flags=0;while(1){if((stack_addr=(void*)malloc(stacksize))==NULL){printf("malloc Stack Failed\n");}pthread_attr_setstacksize(&attr,stacksize);if((err=pthread_create(&tid,&attr,thread_job,NULL))>0){printf("Create failed:%s\n",strerror(err));exit(0);}printf("Thread %d 创建成功\n",++flags);}return 0;
}

由结果可知,当将默认大小变小后,32位系统下可以创建的线程数量增加了。

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

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

相关文章

云层区分神经网络模型——二分类

云层区分神经网络模型——二分类 问奶奶&#xff0c;是什么让他们维护一份感情长达年&#xff0c;奶奶说那个年代什么东西坏了都会想要修&#xff0c;现在什么坏了都想着换。 安装依赖 # 要运行脚本&#xff0c;请先安装以下库&#xff1a;pip install tensorflowpip install …

JAVA每日作业day6.26

ok了家人们&#xff0c;今天我们学习了面向对象-多态&#xff0c;话不多说我们一起来看看吧 一.多态概述 面向对象的第三大特性&#xff1a;封装、继承、多态 我们拿一个生活中的例子来看 生活中&#xff0c;比如跑的动作&#xff0c;小猫、小狗和大象&#xff0c;跑起来是不一…

山水风景视频素材去哪里下?去哪里找?山水风景下载网站分享

在这个数字时代&#xff0c;视频已经成为最直观、有效的传达情感和分享故事的工具。对于那些渴望通过视频传递视觉美感和情感共鸣的创作者来说&#xff0c;拥有高质量的山水风景视频素材是关键。互联网虽然是一个信息量庞大的平台&#xff0c;但找到令人赞叹的山水风景视频素材…

【Linux】使用ntpdate同步时间

ntpdate 是一个在 Linux 系统中用于同步系统时间的命令行工具&#xff0c;它通过与 NTP 服务器通信来调整本地系统时钟。然而&#xff0c;需要注意的是&#xff0c;ntpdate 已经被许多现代 Linux 发行版弃用。 安装 yum install -y ntpdate 查看时间 date同步时间 ntpdate ntp…

问界M9累计大定破10万,创中国豪车新纪录

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 6月26日消息&#xff0c;华为常务董事、终端BG董事长、智能汽车解决方案BU董事长余承东今日宣布&#xff0c;问界M9上市6个月&#xff0c;累计大定突破10万辆。 这一成绩&#xff0c;也创造了中国市场…

postman汉化中文(Windows)

Postman 是一款专业的 API 开发工具&#xff0c;为开发者提供了创建、测试、调试和分享 HTTP 请求的便利性和灵活性。其主要功能包括请求构建与发送、自动化测试、团队协作与分享、实时监视与调试以及环境与变量管理。无论是个人开发者还是团队&#xff0c;Postman 都能有效地提…

深入了解 msvcr120.dll问题解决指南,msvcr120.dll在电脑中的重要性

在Windows操作系统中&#xff0c;.dll 文件扮演了非常重要的角色&#xff0c;它们包含许多程序运行所需的代码和数据。其中 msvcr120.dll 是一个常见的动态链接库文件&#xff0c;是 Microsoft Visual C Redistributable Packages 的一部分。这篇文章将探讨 msvcr120.dll 的功能…

使用Python进行并发和并行编程:提高效率的秘诀

使用Python进行并发和并行编程&#xff1a;提高效率的秘诀 ​ 大家好&#xff0c;今天我们来聊聊如何使用Python进行并发和并行编程&#xff0c;以提升数据处理的效率&#xff1b;在之前的文章中&#xff0c;我们探讨了Python的函数式编程和数据流处理。今天&#xff0c;我们将…

C++用Crow实现一个简单的Web程序,实现动态页面,向页面中输入数据并展示

Crow是一个轻量级、快速的C微框架&#xff0c;用于构建Web应用程序和RESTful API。 将处理前端页面的POST请求以添加数据的逻辑添加到 /submit 路由中&#xff0c;并添加了一个新的路由 / 用于返回包含输入框、按钮和表格的完整页面。当用户向表格添加数据时&#xff0c;JavaS…

SpringAOP执行流程——从源码画流程图

文章目录 了解ProxyFactory入门操作添加多个Advice的执行顺序关于异常Advice关于proceed()方法指定方法才进行增强逻辑 创建代理对象的其他方式ProxyFactoryBeanBeanNameAutoProxyCreatorDefaultAdvisorAutoProxyCreator 对SpringAOP的理解TargetSourceProxyFactory选择JDK/CJL…

【教学类-64-05】20240625彩棒鱼骨图(二)AB排列 6.5*1CM 6选2根 30种

背景需求&#xff1a; 【教学类-64-04】20240619彩棒鱼骨图&#xff08;一&#xff09;6.5*1CM 6根棒子720种-CSDN博客文章浏览阅读897次&#xff0c;点赞23次&#xff0c;收藏13次。【教学类-64-04】20240619彩棒鱼骨图&#xff08;一&#xff09;6.5*1CM 6根棒子720种https:…

JetBrains Rider 2024安装教程

一、下载Rider 1、进入官网&#xff0c;点击“下载” 2、下载完毕 二、安装Rider 1、双击下载的exe文件 2、点击“下一步” 3、可以点击“浏览”选择安装路径&#xff0c;之后点击“下一步” 4、选中图中四项&#xff0c;点击“下一步” 5、选中图中四项&#xff0c;点击“下…

Go语言学习:每日一练1

Go语言学习&#xff1a;每日一练1 目录 Go语言学习&#xff1a;每日一练1变量声明函数定义流程控制 ifrange遍历switch 变量声明 package main//定义变量 var a 1 const Message “hello,world”func main() {b : 2 //短变量声明var c 3c TestMethod(a, b, c)} //定义函数…

进程、CPU、MMU与PCB之间的关系

目录 进程与cpu&#xff08;中央处理器&#xff09; 源代码、程序、cpu与进程的关系 cpu超线程 CPU的简易架构与处理数据过程 进程与MMU&#xff08;内存管理单元&#xff09; mmu作用 cpu和mmu的关系 进程与PCB&#xff08;进程控制块&#xff09; PCB介绍与内部成员…

组合数学、圆排列、离散数学多重集合笔记

自用 如果能帮到您&#xff0c;那也值得高兴 知识点 离散数学经典题目 多重集合组合 补充容斥原理公式 隔板法题目 全排列题目&#xff1a;

Loki部署及使用

简介 loki 是云原生的日志服务,本文讲解loki的部署,日志接入和查询日志的简单使用。 理论 Loki 分两部分,Loki 是日志引擎部分,Promtail 是收集日志端。 Loki 是主服务器,负责存储日志和处理查询 。 promtail 是代理,负责收集日志并将其发送给 loki 。 promtail 是日志…

武汉星起航:全球化舞台,中国跨境电商品牌力与竞争力双提升

随着全球化步伐的加快和数字技术的迅猛发展&#xff0c;跨境出口电商模式已经成为中国企业海外拓展的重要战略选择。这一模式不仅为中国的中小型企业提供了进军全球市场的机会&#xff0c;更为它们在全球舞台上展示独特的竞争优势提供了强有力的支撑。武汉星起航将从市场拓宽、…

江协科技51单片机学习- p19 串口通信

前言&#xff1a; 本文是根据哔哩哔哩网站上“江协科技51单片机”视频的学习笔记&#xff0c;在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了江协科技51单片机教学视频和链接中的内容。 引用&#xff1a; 51单片机入门教程-2…

Java Stream API揭秘:掌握List流操作,打造高效数据处理流程

序言 Java Stream API是Java 8中引入的一个非常重要的功能组成部分&#xff0c;它提供了一种声明式的处理数据集合的方法。它主要特点是基于函数式编程的理念&#xff0c;允许我们以更加简洁、高效的方式进行集合的处理、转换和过滤。通过Stream API&#xff0c;我们可以灵活地…

【LangChain系列——案例分析】【基于SQL+CSV的案例分析】【持续更新中】

目录 前言一、LangChain介绍二、在SQL问答时如何更好的提示&#xff1f;2-1、安装2-2、SQLite 样例数据2-3、使用langchain与其进行交互2-4、查看模型提示语2-5、提供表定义和示例行2-6、将表信息插入到Prompt中去2-7、添加自然语言->SQL示例2-8、在向量数据库中查找最相关的…