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

初谈Linux多线程--线程控制

在这里插入图片描述

文章目录

  • 线程的概述
    • 理解线程
    • Linux中的线程
    • 重新理解的进程
    • Windows的线程
    • 线程的优点
    • 线程的缺点
      • 理解线程调度成本低
    • 进程VS线程
  • 线程控制
    • 创建线程
    • 等待线程
    • 线程函数传参
    • 线程的返回值
      • 新线程的返回值
      • 新线程返回值错误
      • 返回值为类对象
    • 创建多线程
    • 线程的终止
    • 线程的分离pthread_detach

线程的概述

理解线程

线程:线程是在进程内部(PCB)运行,是CPU内部调度的基本单位。

在这里插入图片描述


Linux中的线程

在Linux中,线程执行的是进程代码的一部分,也就是说,线程是进程的实体,可以看作是进程内的一个执行单元,我们将这些不同的执行单元称之为轻量级进程,不同线程之间可以通过共享内存来进行通信。
在这里插入图片描述

这里可以举一个例子,要想有一个幸福的家庭,家庭成员就要执行好自己的事情,这样才能成就一个幸福的家庭。

PCB大部分属性都包含了线程的属性,复用PCB,用PCB统一表示执行流,这样线程就不需要给线程单独设计数据结构和调度算法。这样维护的数据结构和调度算法的代码就少。

在CPU看来,不需要区分task_struct是进程还是线程,都叫做执行流。Linux执行流都是轻量级进程。Linux使用进程模拟线程。


重新理解的进程

以前我们学习的进程=内核数据结构+进程的数据代码,这是我们之前理解的。以前我们强调的是代码的执行流,内部只有一个执行流,而现在我们的内部有多个执行流。

因此以内核观点,给进程重新下一个定义:承担分配系统资源的基本实体。


Windows的线程

操作系统设计一个线程,需要新建、创建、销毁、管理等,线程要不要和进程产生关系呢?
操作系统需要对线程进行管理,先描述(struct tcb),再组织。

struct tcb
{//线程的id,优先级,状态,上下文,连接属性...
}

Windows提供了真实的线程控制块,不是复用PCB属性,这样维护的数据结构和调度算法的代码就高。


线程的优点

  • 创建一个新线程的代价要比创建一个新进程小得多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源要比进程少很多
  • 能充分利用多处理器的可并行数量
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作

线程的缺点

  • 性能损失
    一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型
    线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的
    同步和调度开销,而可用的资源不变。

  • 健壮性降低
    编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了
    不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

  • 缺乏访问控制
    进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

  • 编程难度提高
    编写与调试一个多线程程序比单线程程序困难得多


理解线程调度成本低

线程在同一个进程内部共享相同的地址空间和大部分资源,因此在创建、销毁或者切换线程时,无需像进程那样复制和维护额外的资源。这减少了资源管理的开销。

硬件角度:线程在同一核心上连续执行时,由于其数据可能保持在该核心的缓存中,可以更高效地利用缓存,从而提高数据访问的速度。这可以减少因缓存未命中而引起的额外延迟,进而降低线程切换的成本。


进程VS线程

  • 进程是资源分配的基本单位
  • 线程是调度的基本单位
  • 线程共享进程数据,但也拥有自己的一部分数据:
    线程ID
    一组寄存器:硬件上下文数据,体现出线程可以动态运行
    :线程在运行的时候,本质是运行函数,会形成临时变量,临时变量会被保存在每个线程的栈区
    errno
    信号屏蔽字
    调度优先级

进程的多个线程共享 同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程
中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
文件描述符表、每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)、当前工作目录、用户id和组id。

进程和线程的关系:

在这里插入图片描述

线程控制

在Linux系统中没有线程,只有轻量级进程,这个轻量级进程实际上就是线程,因为没有单独设计TCB。因此Linux操作系统不会直接给我们提供线程的系统调用,只会提供创建轻量级进程的系统调用接口。Linux系统存在一个中间软件层,有一个pthred库,是自带的原生线程库,对该轻量级进程接口进行封装,按照线程的接口提供给用户。所以说,Linux是用户级线程,Windows是内核级线程。

创建线程

创建一个进程的函数接口:

#include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);Compile and link with -pthread.

在这里插入图片描述

参数:
thread:返回线程ID,是一个输出型参数
attr:设置线程的属性,attrNULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数

返回值:成功返回0;失败返回错误码,内容未定义。

等待线程

等待线程的函数接口:

#include <pthread.h>int pthread_join(pthread_t thread, void**retval);Compile and link with -pthread.

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

代码示例:

#include<iostream>
#include<string>
#include<pthread.h>
#include<unistd.h>void *threadRun(void *args)
{int cnt=10;while(cnt){std::cout<<"new thread run ...,cnt:"<<cnt--<<std::endl;sleep(1);}return nullptr;
}int main()
{pthread_t tid;int n=pthread_create(&tid,nullptr,threadRun,(void*)"thread 1");if(n!=0){std::cerr<<"create thread error"<<std::endl;return 1;}std::cout<<"main thread join begin..."<<std::endl;n=pthread_join(tid,nullptr); //保证main线程最后退出if(n==0){std::cout<<"main thread wait sucess"<<std::endl;}return 0;
}

在这里插入图片描述

上述代码中,子线程完成之后,主线程才退出。

这里的tid是什么?
tid是线程库中的一个地址,是虚拟地址。
在这里插入图片描述

线程函数传参

主线程数据可传递给新线程:
代码演示:

#include<iostream>
#include<string>
#include<pthread.h>
#include<unistd.h>void *threadRun(void *args)
{//std::string name=(const char*)args;int a=*(int*)args;int cnt=10;while(cnt){std::cout<<a<<" run ...,cnt:"<<cnt--<<std::endl;sleep(1);}return nullptr;
}std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}int main()
{pthread_t tid;int a=100;int n=pthread_create(&tid,nullptr,threadRun,(void*)&a);if(n!=0){std::cerr<<"create thread error"<<std::endl;return 1;}std::string tid_str=PrintToHex(tid);std::cout<<"tid:"<<tid_str<<std::endl;std::cout<<"main thread join begin..."<<std::endl;n=pthread_join(tid,nullptr); //保证main线程最后退出if(n==0){std::cout<<"main thread wait sucess"<<std::endl;}return 0;      
}

在这里插入图片描述


这里传递参数可以是任意类型,可以传递类对象,在传递类对象时,传递的是地址。也就是说,我们可以传递多个参数。

代码示例:

#include<iostream>
#include<string>
#include<pthread.h>
#include<unistd.h>class ThreadData
{
public:std::string name;int num;
};void *threadRun(void *args)
{//std::string name=(const char*)args;//int a=*(int*)args;ThreadData *td=static_cast<ThreadData*>(args);//(ThreadData*)args  int cnt=10;while(cnt){std::cout<<td->name<<" run ...,num is:"<<td->num<<",cnt:"<<cnt--<<std::endl;sleep(1);}return nullptr;
}std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}int main()
{pthread_t tid;ThreadData td;td.name="thread-1";td.num=1;int n=pthread_create(&tid,nullptr,threadRun,(void*)&td);if(n!=0){std::cerr<<"create thread error"<<std::endl;return 1;}std::string tid_str=PrintToHex(tid);std::cout<<"tid:"<<tid_str<<std::endl;std::cout<<"main thread join begin..."<<std::endl;n=pthread_join(tid,nullptr); //保证main线程最后退出if(n==0){std::cout<<"main thread wait sucess"<<std::endl;}return 0;      
}

在这里插入图片描述

上述做法不推荐,td在主线程中,破坏了主线程的完整性和独立性。td 是一个局部变量,其生命周期仅限于 main 函数。一旦 main 函数结束,td 将会被销毁,此时新线程仍然可能在尝试访问已经无效的内存,从而导致未定义行为。另外,如果此时还有一个线程,使用的也是td访问的也是主函数中td变量,那这两个线程中如果其中一个线程对td进行修改,那么就会影响另一个线程。

换一种做法:

在主线程中申请一个堆上的空间,把堆上的地址拷贝给线程,此时对空间就交给了新线程。如果还有第二个线程,那就重新new一个堆上的空间,交给新的线程。这样每一个线程都有属于自己的堆空间。另外,在线程内部处理完后需要delete

#include<iostream>
#include<string>
#include<pthread.h>
#include<unistd.h>class ThreadData
{
public:std::string name;int num;
};void *threadRun(void *args)
{//std::string name=(const char*)args;//int a=*(int*)args;ThreadData *td=static_cast<ThreadData*>(args);//(ThreadData*)args  int cnt=10;while(cnt){std::cout<<td->name<<" run ...,num is:"<<td->num<<",cnt:"<<cnt--<<std::endl;sleep(1);}delete td;return nullptr;
}std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}int main()
{pthread_t tid;ThreadData *td=new ThreadData(); //在堆上申请空间td->name="thread-1";td->num=1;int n=pthread_create(&tid,nullptr,threadRun,td);if(n!=0){std::cerr<<"create thread error"<<std::endl;return 1;}std::string tid_str=PrintToHex(tid);std::cout<<"tid:"<<tid_str<<std::endl;std::cout<<"main thread join begin..."<<std::endl;n=pthread_join(tid,nullptr); //保证main线程最后退出if(n==0){std::cout<<"main thread wait sucess"<<std::endl;}return 0;      
}

在这里插入图片描述

线程的返回值

新线程的返回值

线程的返回值是void*,那么主线程的函数就必须使用void **的参数返回,int pthread_join(pthread_t thread, void**retval)void**retval是一个输出型参数,需要一个void*类型的变量即void* retvoid* ret是一个指针类型的变量,有对应的空间,在Linux中占8个字节,在传递的时候是传递&ret ,是将地址传过去。

主线程拿到新线程的退出信息:
在这里插入图片描述

在这里插入图片描述

代码:

#include<iostream>
#include<string>
#include<pthread.h>
#include<unistd.h>class ThreadData
{
public:std::string name;int num;
};void *threadRun(void *args)
{//std::string name=(const char*)args;//int a=*(int*)args;ThreadData *td=static_cast<ThreadData*>(args);//(ThreadData*)args  int cnt=10;while(cnt){std::cout<<td->name<<" run ...,num is:"<<td->num<<",cnt:"<<cnt--<<std::endl;sleep(1);}delete td;return (void*)111;
}std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}int main()
{pthread_t tid;ThreadData *td=new ThreadData(); //在堆上申请空间td->name="thread-1";td->num=1;int n=pthread_create(&tid,nullptr,threadRun,td);if(n!=0){std::cerr<<"create thread error"<<std::endl;return 1;}std::string tid_str=PrintToHex(tid);std::cout<<"tid:"<<tid_str<<std::endl;std::cout<<"main thread join begin..."<<std::endl;void* code=nullptr;//开辟了空间n=pthread_join(tid,&code); //保证main线程最后退出if(n==0){std::cout<<"main thread wait sucess,new thread exit code:"<<(uint64_t)code<<std::endl;}return 0;      
}

在这里插入图片描述

可以根据退出信息,就可以判断新线程是否完成对应的任务。

新线程返回值错误

在这里插入图片描述

上述代码故意让新线程出现野指针,是的新线程出现错误。

在这里插入图片描述
上述代码时主线程,新线程出错后让主线程等100s后再退出。

代码演示结果:

在这里插入图片描述

主线程没有等100s后退出,而是在新的进程异常后直接退出。

线程的返回值只有正确时的返回值,一旦出现异常,线程就会崩溃,线程出现异常就会发信号给进程,进程就会被杀掉,即使进程里面有多个线程,里面有一个线程出现错误,整个进程都会被杀掉。
因此线程的在退出的时候只需要考虑正确的返回,不考虑异常,一旦异常,整个进程都会崩溃,包括主线程。

返回值为类对象

主线程创建并启动了一个新的线程,通过 pthread_create 和 pthread_join 实现了线程的创建和等待。
在新线程中,通过 ThreadData 类传递了操作数,并在 threadRun 函数中计算了加法操作的结果。
最后,主线程将计算结果打印出来,展示了线程之间数据的传递和简单的同步操作。

#include<iostream>
#include<string>
#include<pthread.h>
#include<unistd.h>class ThreadData
{
public:int Excute(){return x+y;}
public:std::string name;int x;int y;
};class ThreadResult
{
public:std::string print(){return std::to_string(x)+"+"+std::to_string(y)+"="+std::to_string(result);}
public:int x;int y;int result;
};void *threadRun(void *args)
{//std::string name=(const char*)args;//int a=*(int*)args;ThreadData *td=static_cast<ThreadData*>(args);//(ThreadData*)args  ThreadResult *result=new ThreadResult();int cnt=10;while(cnt){sleep(1);std::cout<<td->name<<" run ..."<<",cnt:"<<cnt--<<std::endl;result->result=td->Excute();result->x=td->x;result->y=td->y;break;}delete td;return (void*)result;
}std::string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}int main()
{pthread_t tid;ThreadData *td=new ThreadData(); //在堆上申请空间td->name="thread-1";td->x=10;td->y=20;int n=pthread_create(&tid,nullptr,threadRun,td);if(n!=0){std::cerr<<"create thread error"<<std::endl;return 1;}std::string tid_str=PrintToHex(tid);std::cout<<"tid:"<<tid_str<<std::endl;std::cout<<"main thread join begin..."<<std::endl;ThreadResult* result=nullptr;//开辟了空间n=pthread_join(tid,(void**)&result); //保证main线程最后退出if(n==0){std::cout<<"main thread wait sucess,new thread exit code:"<<result->print()<<std::endl;}return 0;      
}

在这里插入图片描述

创建多线程

#include<iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include<unistd.h>using namespace std;const int num=10;void *threadrun(void* args)
{string name=static_cast<const char*>(args);while(true){cout<<name<<"is running"<<endl;sleep(1);break;}return args;
}string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}int main()
{vector<pthread_t> tids;for(int i=0;i<num;i++){pthread_t tid;char *name=new char[128];snprintf(name,128,"thread-%d",i+1);//名字pthread_create(&tid,nullptr,threadrun,name); //传递线程的名字//保存所有线程的id信息tids.push_back(tid);}//等待for(auto tid:tids){void *name=nullptr;pthread_join(tid,&name);//cout<<PrintToHex(tid)<<" quit..."<<endl;cout<<(const char*)name<<" quit..."<<endl;delete (const char*)name; }sleep(1);    return 0;
}

在主函数中,首先创建一个 vector 容器 tids,用于存储所有线程的 ID。使用for循环创建num个线程。在第一个for循环中,配一个新的字符数组name来存储线程名字,使用 snprintf 将线程名字格式化为 thread-i 的形式,调用 pthread_create 函数创建线程,传递线程名字作为参数,将每个线程的 ID 存入 tids 向量。第二个for循环中,等待所有进程结束,使用 pthread_join 函数等待线程结束,获取线程返回的 name,并输出线程名字加上 “quit…”,删除线程名字的内存,以防止内存泄漏。

运行结果:
在这里插入图片描述

线程的终止

新的线程如何终止?函数return

主线程如何终止?
主线程对应的main函数结束,那么主线程结束,表示整个进程结束。此时如果还有新线程还没有结束,那么新线程就会被提前终止,因此有线程等待,是的主线程最后结束。

使用线程终止函数:

#include <pthread.h>void pthread_exit(void *retval);Compile and link with -pthread.

代码测试:

#include<iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>using namespace std;const int num=10;void *threadrun(void* args)
{string name=static_cast<const char*>(args);while(true){cout<<name<<"is running"<<endl;sleep(3);break;}//exit(1);//exit是进程退出,不能终止线程//return args;pthread_exit(args);
}string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}int main()
{vector<pthread_t> tids;for(int i=0;i<num;i++){pthread_t tid;char *name=new char[128];snprintf(name,128,"thread-%d",i+1);//名字pthread_create(&tid,nullptr,threadrun,name); //传递线程的名字//保存所有线程的id信息//tids.push_back(tid);tids.emplace_back(tid);}//等待for(auto tid:tids){void *name=nullptr;pthread_join(tid,&name);//cout<<PrintToHex(tid)<<" quit..."<<endl;cout<<(const char*)name<<" quit..."<<endl;delete (const char*)name; }sleep(1);    return 0;
}

在这里插入图片描述


还有一个终止线程函数:

#include <pthread.h>int pthread_cancel(pthread_t thread);Compile and link with -pthread.

这个实际上是取消一个线程,发送一个取消请求给线程,一般都是使用主线程取消其他线程。

代码示例:

#include<iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>using namespace std;const int num=10;void *threadrun(void* args)
{string name=static_cast<const char*>(args);while(true){cout<<name<<"is running"<<endl;sleep(1);//break;}//exit(1);//exit是进程退出,不能终止线程//return args;pthread_exit(args);
}string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}int main()
{vector<pthread_t> tids;for(int i=0;i<num;i++){pthread_t tid;char *name=new char[128];snprintf(name,128,"thread-%d",i+1);//名字pthread_create(&tid,nullptr,threadrun,name); //传递线程的名字//保存所有线程的id信息//tids.push_back(tid);tids.emplace_back(tid);}sleep(5);//等待for(auto tid:tids){pthread_cancel(tid);//取消cout<<"cancel:"<<PrintToHex(tid)<<endl;void *result=nullptr;  //现成被取消,被取消的线程的返回结果是-1pthread_join(tid,&result);//cout<<PrintToHex(tid)<<" quit..."<<endl;cout<<(long long int)result<<" quit..."<<endl;//delete (const char*)name; }sleep(1);    return 0;
}

在这里插入图片描述

线程的分离pthread_detach

线程的分离函数接口:

#include <pthread.h>int pthread_detach(pthread_t thread);Compile and link with -pthread.

如果一个线程被创建,默认是需要joinable的,必须被join;如果一个线程被分离,线程的工作状态是分离状态,不能被join

进程分离:一个线程依旧属于线程,但是不需要被主线程等待。

#include<iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>using namespace std;const int num=10;void *threadrun(void* args)
{pthread_detach(pthread_self());string name=static_cast<const char*>(args);while(true){cout<<name<<"is running"<<endl;sleep(3);break;}//exit(1);//exit是进程退出,不能终止线程//return args;pthread_exit(args);
}string PrintToHex(pthread_t &tid)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%lx",tid);return buffer;
}int main()
{vector<pthread_t> tids;for(int i=0;i<num;i++){pthread_t tid;char *name=new char[128];snprintf(name,128,"thread-%d",i+1);//名字pthread_create(&tid,nullptr,threadrun,name); //传递线程的名字//保存所有线程的id信息//tids.push_back(tid);tids.emplace_back(tid);}//等待for(auto tid:tids){void *result=nullptr;  //现成被取消,被取消的线程的返回结果是-1int n=pthread_join(tid,&result);//cout<<PrintToHex(tid)<<" quit..."<<endl;cout<<(long long int)result<<" quit...,n:"<<n<<endl;//delete (const char*)name; }sleep(100);    return 0;
}

在这里插入图片描述

上述代码表示:主线程创建一个新线程,但是新的线程被分离了,没有被等待,因此n返回为22。.

如果被分离的线程出异常依然会影响进程,会导致进程直接崩溃。

在这里插入图片描述

相关文章:

初谈Linux多线程--线程控制

文章目录 线程的概述理解线程Linux中的线程重新理解的进程Windows的线程线程的优点线程的缺点理解线程调度成本低 进程VS线程 线程控制创建线程等待线程线程函数传参线程的返回值新线程的返回值新线程返回值错误返回值为类对象 创建多线程线程的终止线程的分离pthread_detach 线…...

文件工具类 - FileUtils

Slf4j Component public class FileUtils {/*** 文件夹复制到指定的文件夹*/SneakyThrowspublic static void copyDir(File source, File target) {if (!target.exists()) {boolean mkdirs target.mkdirs();}if (source.isDirectory()) {File[] files source.listFiles();if …...

Kafka源码剖析-Producer基于内存缓存池分配ByteBuffer

文章目录 在将消息发送到内存缓中区之前做的准备工作发送消息前的准备工作代码示例源码分析1. **消息序列化**2. **元数据准备**3. **分区选择**4. **批处理准备**总结大致浏览一下源码中将消息写入内存缓冲的运行流程源码分析1. **消息序列化和创建记录批次**2. **确定分区**3…...

实习十九:学习笔记

上午 1、构建vue发行版本 [rootserver ~]# cd eleme_web/ [rootserver eleme_web]# npm run buid //项目未执行时运行该命令&#xff0c;创建发行版本 [rootserver eleme_web]# cd dist/ //dist中包含发行版本的所有文件 [rootserver dist]# ls css favicon.ico i…...

OrionX:革新GPU资源管理,助力AI开发团队高效运作

您的AI开发团队是否经常陷入这样的窘境&#xff1a; 人多卡少&#xff0c;GPU资源难以满足每个成员的需求&#xff1f; 当开发环境中需要变更GPU卡配置时&#xff0c;流程繁琐不堪&#xff0c;不得不关闭容器、重新配置再重启&#xff1f; 是否曾因GPU卡分配后未被充分利用而…...

RabbitMQ发送者重连、发送者确认

RabbitMQ发送者重连、发送者确认 一、发送者重连 spring:rabbitmq:connection-timeout: 1s #设置MQ的连接超时时间template:retry:enabled: true #开启超时重试机制initial-interval: 1000ms #失败后的初始等待时间multiplier: 1 #失败后下次的等待时长倍数&#xff0c;下次等…...

请转告HPC计算AI计算单位,选对存储事半功倍

U.2 NVMe全闪混合统一存储GS 5000U是Infortrend产品中一款高性能机型。得益于搭载强劲的第五代IntelXeon处理器&#xff0c;以及支持PCIe 5.0、NVMe-oF、100GbE等多种特点&#xff0c;GS 5000U单台块级性能可达50 GB/s的读、20 GB/s的写&#xff0c;以及1300K的IOPS&#xff1b…...

[GYCTF2020]Blacklist1

打开题目 判断注入类型&#xff0c;输入1试试 输入2 输入1 判断为字符型注入 堆叠查询2;show databases;# 然后来输入2; show tables;#来查看数据库的表 然后我们通过FlagHere表来查看列输入2;show columns from FlagHere;# 来查看列 、 重新构造payload&#xff1a;0;HAND…...

Blackcat V2.2付费会员制WordPress资源站主题

Blackcat-付费会员制WordPress资源站主题&#xff0c;该主题是基于简约实用的主题选项框架 Codestar Framework 进行开发的功能强大的付费会员制主题&#xff0c;该主题尤其适合用于搭建付费下载资源网站&#xff0c;比如素材站、软件站、视频教程站等付费资源下载网站。 集成…...

动手学强化学习 第 18 章 离线强化学习 训练代码

基于 https://github.com/boyu-ai/Hands-on-RL/blob/main/%E7%AC%AC18%E7%AB%A0-%E7%A6%BB%E7%BA%BF%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0.ipynb 理论 离线强化学习 修改了警告和报错 运行环境 Debian GNU/Linux 12 Python 3.9.19 torch 2.0.1 gym 0.26.2 运行代码 CQL.…...

Python笔试面试题AI答之面向对象常考知识点

Python面向对象面试题面试题覆盖了Python面向对象编程&#xff08;OOP&#xff09;的多个重要概念和技巧&#xff0c;包括元类&#xff08;Metaclass&#xff09;、自省&#xff08;Introspection&#xff09;、面向切面编程&#xff08;AOP&#xff09;和装饰器、重载&#xf…...

面试经典算法150题系列-数组/字符串操作之买卖股票最佳时机

买卖股票最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易…...

安装jdk和tomcat

安装nodejs 1.安装nodejs&#xff0c;这是一个jdk一样的软件运行环境 yum -y list installed|grep epel yum -y install nodejs node -v 2.下载对应的nodejs软件npm yum -y install npm npm -v npm set config .....淘宝镜像 3.安装vue/cli command line interface 命令行接…...

mongodb 备份还原

### 加入 MongoDB 官方 repositoryecho [mongodb-org-4.4] nameMongoDB Repository baseurlhttps://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.4/x86_64/ gpgcheck1 enabled1 gpgkeyhttps://www.mongodb.org/static/pgp/server-4.4.asc| tee /etc/yum.repos.d/mo…...

day27——homework

1、使用两个线程完成两个文件的拷贝&#xff0c;分支线程1拷贝前一半&#xff0c;分支线程2拷贝后一半&#xff0c;主线程回收两个分支线程的资源 #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <fcntl.h> #include <uni…...

shell脚本自动化部署

1、自动化部署DNS [rootweb ~]# vim dns.sh [roottomcat ~]# yum -y install bind-utils [roottomcat ~]# echo "nameserver 192.168.8.132" > /etc/resolv.conf [roottomcat ~]# nslookup www.a.com 2、自动化部署rsync [rootweb ~]# vim rsync.sh [rootweb ~]# …...

C语言| 文件操作详解(二)

目录 四、有关文件的随机读写函数 4.1 fseek 4.2 ftell 4.3 rewind 五、判定文件读取结束的标准与读写文件中途发生错误的解决办法 5.1 判定文件读取结束的标准 5.2 函数ferror与feof 5.2.1 函数ferror 5.2.2 函数feof 在上一章中&#xff0c;我们主要介绍了文件类型…...

保证项目如期上线,测试人能做些什么?

要保证项目按照正常进度发布&#xff0c;需要整个研发团队齐心协力。 有很多原因都可能会造成项目延期。 1、产品经理频繁修改需求 2、开发团队存在技术难题 3、测试团队测不完 今天我想跟大家聊一下&#xff0c;测试团队如何保证项目按期上线&#xff0c;以及在这个过程中可能…...

【杂谈】在大学如何学得计算机知识,浅谈大一经验总结

大学新生的入门经验简谈 我想在学习编程这条路上&#xff0c;很多同学感到些许困惑&#xff0c;摸爬滚打一年&#xff0c;转眼就要进入大二学习了&#xff0c;下面浅谈个人经验与反思总结。倘若说你是迷茫的&#xff0c;希望这点经验对你有帮助&#xff1b;但倘若你有更好的建…...

Superset二次开发之柱状图实现同时显示百分比、原始值、汇总值的功能

背景 柱状图贡献模式选择行,堆积样式选择Stack,默认展示百分比,可以展示每个堆积的百分比,但是无法实现同时展示百分比、原始值、汇总值的效果。借助Tooltip可以实现,但是不直观。 柱状图来自Echarts插件,可以先考虑Echarts的柱状图如何实现此需求,再研究Superset项目的…...

堆的创建和说明

文章目录 目录 文章目录 前言 小堆&#xff1a; 大堆&#xff1a; 二、使用步骤 1.创建二叉树 2.修改为堆 3.向上调整 结果实现 总结 前言 我们已经知道了二叉树的样子&#xff0c;但是一般的二叉树是没有什么意义的&#xff0c;所以我们会使用一些特殊的二叉树来进行实现&a…...

【玩转python】入门篇day14-函数

1、函数的定义 函数通过def定义&#xff0c;包括函数名、参数、返回值 # 定义函数 def test(a,b): # a,b表示形式参数print(a b)#函数体(具体的功能)return a*b #返回值# 函数调用 test(12,43) # 12和43表示实际参数,在调用函数时,会替换形式参数a,b下面这个展示了稍微复…...

uni-app 将base64图片转换成临时地址

function getTempFilePath(base64Data) {return new Promise((resolve, reject) > {const fs uni.getFileSystemManager()base64Data base64Data.split(,)[1]const fileName temp_image_ Date.now() .png // 自定义文件名&#xff0c;可根据需要修改const filePath un…...

C#用Socket实现TCP客户端

1、TCP客户端实现代码 using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks;namespace PtLib.TcpClient {public delegate void Tcp…...

jmeter-beanshell学习15-输入日期,计算前后几天的日期

又遇到新问题了&#xff0c;想要根据获取的日期&#xff0c;计算出前面两天的日期。网上找了半天&#xff0c;全都是写获取当天日期&#xff0c;然后计算昨天的日期&#xff0c;照葫芦画瓢也没改出来想要的&#xff0c;最后求助了开发同学。 先放上网上获取当天&#xff0c;计…...

Zabbix 7.0 安装

在zabbix官网中有着比较完善的安装步骤&#xff0c;针对不同的系统都有。可以直接按照举例说明进行安装。本文只是针对其提供的安装步骤进行一些说明解释补充。 安装环境 操作系统版本&#xff1a;AlmaLinux 9.4&#xff08;10.10.20.200&#xff09;zabbix版本&#xff1a;7.…...

软考高级-系统架构设计师

2024广东深圳考试时间 报考人员可登录中国计算机技术职业资格网&#xff08;http://www.ruankao.org.cn&#xff09;进行网上报名&#xff0c;报名前须扫码绑定个人微信&#xff0c;不允许代报名。 上半年考试报名信息填报时间&#xff1a;2024年3月25日9:00&#xff0d;4月2日…...

Notepad++ 安装 compare 插件

文章目录 文章介绍对比效果安装过程参考链接 文章介绍 compare 插件用于对比文本差异 对比效果 安装过程 搜索compare插件 参考链接 添加链接描述...

大数据技术原理-spark的安装

摘要 本实验报告详细记录了在"大数据技术原理"课程中进行的Spark安装与应用实验。实验环境包括Spark、Hadoop和Java。实验内容涵盖了Spark的安装、配置、启动&#xff0c;以及使用Spark进行基本的数据操作&#xff0c;如读取本地文件、文件内容计数、模式匹配和行数…...

第四范式上线搜广推一体化平台 赋能企业高效增长

产品上新 Product Release 今天&#xff0c;第四范式产品再度上新&#xff0c;正式升级并推出的“搜广推”一体化平台——天枢。 天枢拥有全面的用户画像分析、端到端的搜索推荐一体化、一站式流量运营管理等能力&#xff0c;集合智能搜索、智能推荐和智能推广三大能力于一身&a…...

惠州做棋牌网站建设哪家公司收费合理/百度营销后台

文章目录**1. 概述****2. String类的特点****3. 常见操作方法****3.1 构造方法****3.2 判断功能****3.3 获取功能****3.4 转换功能****3.5 其他功能****4. String类练习**4.1 把数组中的数据按照指定个格式拼接成一个字符串**4.2 字符串反转****4.3 统计大串中小串出现的次数**…...

网站开发去哪里找程序员/项目外包平台

版权声明:本文为 小异常 原创文章,非商用自由转载-保持署名-注明出处,谢谢! 本文网址:https://sunkuan.blog.csdn.net/article/details/111676037 文章目录 一、uni指令二、数据绑定三、事件的使用本篇博客主要讲解 uni-app 的中指令、数据绑定及事件的使用,只要有 Vue 基…...

哈尔滨免费建站模板/个人网站注册平台

一.角色以及入口&#xff1a; 角色&#xff1a;系统管理员&#xff0c;开发人员 入口&#xff1a;服务管理--修改服务 二.页面元素检查&#xff1a; 对页面初始化的检查,即页面打开后,对页面不做任何操作时的元素检查。&#xff08;破页&#xff1b;js错&#xff1b;demo对比等…...

2b2网站开发/百度移动端优化

sery 的BLOG链接:http://sery.blog.ccidnet.com/blog/ccid/do_showone/tid_35445.html来自 “ ITPUB博客 ” &#xff0c;链接&#xff1a;http://blog.itpub.net/39335/viewspace-350917/&#xff0c;如需转载&#xff0c;请注明出处&#xff0c;否则将追究法律责任。 转载于…...

宽屏网站背景/网络营销推广软件

Map JavaScript 的对象&#xff08;Object&#xff09;&#xff0c;本质上是键值对的集合&#xff08;Hash 结构&#xff09;&#xff0c;但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。为了解决这个问题&#xff0c;ES6 提供了 Map 数据结构。它类似于对象&am…...

网站推广的主要方法/seo网站seo

楼主工作的单位是一家欧洲公司&#xff0c;主营奢侈品的生产和销售&#xff0c;我们有一个PLM&#xff08;产品生命周期管理系统&#xff09;&#xff0c;用来管理产品的主数据&#xff0c;例如对部品及物料从设计到生产&#xff0c;以及BOM等主数据的管理&#xff0c;我们采购…...