Linux-线程知识点

news2024/11/20 23:23:41

目录

  • 线程与进程区别
  • pthread库接口介绍
  • pthread_create
  • pthread_self和syscall(SYS_gettid);
  • pthread_equal
  • 测试主线程的栈空间大概是多大
  • pthread_setname_np
  • pthread_exit
  • pthread_join
    • 为什么要连接退出的线程
  • pthread_detach

线程与进程区别

进程是一个动态的实体,有自己的生命周期。线程是操作系统进程调度器可以调度的最小执行单位。

一个进程可能包含多个线程。

进程之间,彼此的地址空间是独立的,但同一个进程的多个线程共享内存地址空间,即全局内存区域,包括初始化数据段、未初始化数据段和动态分配的内存段。

这种共享给线程带来了更多的优势:

  • 创建线程花费的时间要比创建进程花费的时间少
  • 终止线程花费的时间要比终止进程花费的时间少
  • 线程之间上下文切换的开销要比进程之间上下文切换的开销小
  • 线程之间共享数据要比进程之间共享数据简单

线程共享地址空间的设计,让多个线程之间的通信变得非常简单。一个进程内的多个线程,就像一个软件研发小组内部的不同员工,共享代码、服务器、打印机、资料,彼此之间有分工协作,沟通协作成本比较低。进程之间的通信代价则要高很多。进程之间不得不采用一些进程间通信的手段(如管道、共享内存及信号量等)来协作。

需要强调的一点是,线程和进程不一样,进程有父进程的概念,但在线程组里,所有的线程都是对等的关系:

  • 并不是只有主线程才能创建线程,被创建出来的线程同样可以创建线程。
  • 不存在类似于fork函数那样的父子关系,大家都归属于同一个线程组,进程ID都相等,而且各有各的线程ID。
  • 并非只有主线程才能调用pthread_join连接其他线程,同一线程组内的任意线程都可以对某线程执行pthread_join函数。
  • 并非只有主线程才能调用pthread_detach函数,其实任意线程都可以对同一线程组内的线程执行分离操作。

pthread库接口介绍

POSIX函数函数功能描述
pthread_create创建一个线程
pthread_exit退出线程
pthread_self获取线程ID
pthread_equal检查两个线程ID是否相等
pthread_join等待线程退出
pthread_detach设置线程状态为分离状态
pthread_cancel线程的取消
pthread_setname_np设置线程的名称
pthread_cleanup_push线程退出,清理函数注册和执行
pthread_cleanup_pop线程退出,清理函数注册和执行

pthread_create

程序开始启动的时候,产生的进程只有一个线程,我们称之为主线程或初始线程。对于单线程的进程而言,只存在主线程一个线程。如果想在主线程之外,再创建一个或多个线程,就需要用到这个接口了。

函数接口如下:

#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
                   const pthread_attr_t *restrict attr,
                   void *(*start_routine)(void*),
                   void *restrict arg);

  • pthread_create函数的第一个参数是pthread_t类型的指针,线程创建成功的话,会将分配的线程ID填入该指针指向的地址。线程的后续操作将使用该值作为线程的唯一标识。
  • 第二个参数是pthread_attr_t类型,通过该参数可以定制线程的属性,比如可以指定新建线程栈的大小、调度策略等。如果创建线程无特殊的要求,该值也可以是NULL,表示采用默认属性。
  • 第三个参数是线程需要执行的函数。创建线程,是为了让线程执行一定的任务。线程创建成功之后,该线程就会执行start_routine函数,该函数之于线程,就如同main函数之于主线程。
  • 第四个参数是新建线程执行的start_routine函数的入参。

返回值:如果成功,则pthread_create返回0;如果不成功,则pthread_create返回一个非0的错误码。常见的错误码如表:

返回值描述
EAGAIN系统资源不够,或者创建线程的个数超过系统对一个进程中线程总数的限制
EINVAL第二个参数attr不合法
EPERM没有合适的权限来设置调度策略或参数
// pthread_create示例

#include <iostream>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>

void* ThreadFunc(void* _threadId)
{
    std::cout << "线程ID: " << pthread_self() << " hello world" << std::endl;

    pthread_exit(nullptr);
}

int main()
{
    // 创建五个线程
    pthread_t tid[5];

    for (int i = 0;i < 5;i++)
    {
        int rc = pthread_create(&tid[i], nullptr, ThreadFunc, (void*)tid[i]);
        if (rc != 0)
        {
            std::cout << "线程创建失败" << std::endl;;

            return -1;
        }
    }

    // 等待所有线程结束
    pthread_exit(nullptr);

    return 0;
}

pthread_self和syscall(SYS_gettid);

这两个函数都是获取自身的线程ID

 #include <pthread.h>
 
 pthread_t pthread_self();
 
 // --------------------------------- //
 
 int TID = syscall(SYS_gettid);

区别是:

  • syscall(SYS_gettid),属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一标识该线程。
  • pthread_self(),属于NPTL线程库的范畴,线程库的后续操作,就是根据该线程ID来操作线程的。
// 示例

#include <iostream>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>

void* ThreadFunc(void* _threadId)
{
    int TID = syscall(SYS_gettid);

    std::cout << "TID: " << TID << std::endl;
    std::cout << "pthread_self: " << pthread_self() << std::endl;

    pthread_exit(nullptr);
}

int main()
{
    pthread_t tid;


    int rc = pthread_create(&tid, nullptr, ThreadFunc, (void*)tid);
    if (rc != 0)
    {
        std::cout << "线程创建失败" << std::endl;;

        return -1;
    }

    // 等待线程退出
    int res = pthread_join(tid, nullptr);
    if (res != 0)
    {
        std::cout << strerror(res) << std::endl;

        return -1;
    }

    std::cout << "线程已经退出" << std::endl;

    return 0;
}

[root@Zhn 线程]# g++ pthread_detach.cpp -o pthread_detach -lpthread
[root@Zhn 线程]# ./pthread_detach 
TID: 8718
pthread_self: 140005981333248
线程已经退出
[root@Zhn 线程]# 

pthread_equal

在同一个线程组内,线程库提供了接口,可以判断两个线程ID是否对应着同一个线程:

#include <pthread.h>

int pthread_equal(pthread_t t1, pthread_t t2);

返回值是0的时候,表示两个线程是同一个线程,非零值则表示不是同一个线程。

pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址。

测试主线程的栈空间大概是多大

// 查看一个线程的栈最大占用多少内存空间

#include <iostream>
#include <pthread.h>
#include <unistd.h>

int i = 0;

void func()
{
    // int类型是4个字节,256个int类型是4 * 256 = 1024,也就是1kB
    // 每执行一次func函数就会占用1kB的栈空间,看看可以执行多少次这个函数
    int buffer[256];

    std::cout << "i = " << i << std::endl;

    i++;

    func();
}

int main()
{
    func();

    sleep(100);
}

在这里插入图片描述

可以执行8053次,每次是1kB,也就是8053 / 1024,大概是8MB。

pthread_setname_np

给线程设置名称

#include <pthread.h>

int pthread_setname_np(pthread_t thread, const char *name);
// pthread_setname_np示例

#include <iostream>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>

void* ThreadFunc(void* _threadId)
{
    // 设置线程名称
    pthread_setname_np(pthread_self(), std::to_string(pthread_self()).c_str());

    while (1)
    {
        std::cout << "线程ID: " << pthread_self() << " hello world" << std::endl;
        sleep(1);
    }

    pthread_exit(nullptr);
}

int main()
{
    pthread_t tid[5];

    for (int i = 0;i < 5;i++)
    {
        int rc = pthread_create(&tid[i], nullptr, ThreadFunc, (void*)tid[i]);
        if (rc != 0)
        {
            std::cout << "线程创建失败" << std::endl;;

            return -1;
        }
    }

    // 等待所有线程结束
    pthread_exit(nullptr);

    return 0;
}

创建了五个线程,给每个线程设置名称为自己线程ID。

在这里插入图片描述

CMD字段就是设置的线程名称。

pthread_exit

有生就有灭,线程执行完任务,也需要终止。

下面的三种方法中,线程会终止,但是进程不会终止(如果线程不是进程组里的最后一个线程的话):

  • 创建线程时的start_routine(线程执行函数)函数执行了return,并且返回指定值。
  • 线程调用pthread_exit。
  • 其他线程调用了pthread_cancel函数取消了该线程。如果线程组中的任何一个线程调用了exit函数,或者主线程在main函数中执行了return语句,那么整个线程组内的所有线程都会终止。

pthread_exit函数的定义:

#include <pthread.h>

void pthread_exit(void *value_ptr);

value_ptr是一个指针,存放线程的“临终遗言”。线程组内的其他线程可以通过调用pthread_join函数接收这个地址,从而获取到退出线程的临终遗言。如果线程退出时没有什么遗言,则可以直接传递NULL指针,如下所示:

pthread_exit(NULL);

但是这里有一个问题,就是不能将遗言存放到线程的局部变量里,因为如果用户写的线程函数退出了,线程函数栈上的局部变量可能就不复存在了,线程的临终遗言也就无法被接收者读到。

那我们应该如何正确地传递返回值呢?

  • 如果是int型的变量,则可以使用“pthread_exit((int*)ret);”。
  • 使用全局变量返回。
  • 将返回值填入到用malloc在堆上分配的空间里。·使用字符串常量,如pthread_exit(“hello,world”)。

线程退出有一种比较有意思的场景,即线程组的其他线程仍在执行的情况下,主线程却调用pthread_exit函数退出了。这会发生什么事情?首先要说明的是这不是常规的做法,但是如果真的这样做了,那么主线程将进入僵尸状态,而其他线程则不受影响,会继续执行。

pthread_join

线程库提供了pthread_join函数,用来等待某线程的退出并接收它的返回值。这种操作被称为连接(joining)。

#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

第一个参数表示要等待的线程ID,第二个参数代表线程退出的返回值。

根据等待的线程是否退出,可得到如下两种情况:

  • 等待的线程尚未退出,那么pthread_join的调用线程就会陷入阻塞。
  • 等待的线程已经退出,那么pthread_join函数会将线程的退出值(void*类型)存放到retval指针指向的位置。

线程的连接(join)操作有点类似于进程等待子进程退出的等待(wait)操作,但还是有不同之处:

  • 第一点不同之处是进程之间的等待只能是父进程等待子进程,而线程则不然。线程组内的成员是对等的关系,只要是在一个线程组内,就可以对另外一个线程执行连接(join)操作。
  • 第一点不同之处是进程之间的等待只能是父进程等待子进程,而线程则不然。线程组内的成员是对等的关系,只要是在一个线程组内,就可以对另外一个线程执行连接(join)操作。

返回值:

返回值说明
ESRCH传入的线程ID不存在,查无此线程
EINVAL线程不是一个可连接的线程或者已经有其他线程连接
EDEADLK死锁

pthread_join不能连接线程组内任意线程的做法,并不是NPTL线程库设计上的瑕疵,而是有意为之的。如果听任线程连接线程组内的任意线程,那么所谓的任意线程就会包括其他库函数私自创建的线程,当库函数尝试连接(join)私自创建的线程时,发现已经被连接过了,就会返回EINVAL错误。如果库函数需要根据返回值来确定接下来的流程,这就会引发严重的问题。正确的做法是,连接已知线程ID的那些线程,就像pthread_join函数那样。

pthread_join函数之所以能够判断是否死锁和连接操作是否被其他线程捷足先登,是因为目标线程的控制结构体struct pthread中,存在如下成员变量,记录了该线程的连接者:

struct pthread *joinid;

该指针存在三种可能,如下。

  • NULL:线程是可连接的,但是尚没有其他线程调用pthread_join来连接它。
  • 指向线程自身的struct pthread:表示该线程属于自我了断型,执行过分离操作,或者创建线程时,设置的分离属性为PTHREAD_CREATE_DETACHED,一旦退出,则自动释放所有资源,无需其他线程来连接。
  • 指向线程组内其他线程的struct pthread:表示joinid对应的线程会负责连接。

为什么要连接退出的线程

如果不连接退出的线程,会导致资源无法释放

如果不执行连接操作,线程的资源就不能被释放,也不能被复用,这就造成了资源的泄漏。

值得一提的是,纵然调用了pthread_join,也并没有立即调用munmap来释放掉退出线程的栈,它们是被后建的线程复用了,这是NPTL线程库的设计。释放线程资源的时候,NPTL认为进程可能再次创建线程,而频繁地munmap和mmap会影响性能,所以NTPL将该栈缓存起来,放到一个链表之中,如果有新的创建线程的请求,NPTL会首先在栈缓存链表中寻找空间合适的栈,有的话,直接将该栈分配给新创建的线程。

始终不将线程栈归还给系统也不合适,所有缓存的栈大小有上限,默认是40MB,如果缓存起来的线程栈的空间总和大于40MB,NPTL就会扫描链表中的线程栈,调用munmap将一部分空间归还给系统。

// pthread_join示例

#include <iostream>
#include <pthread.h>
#include <unistd.h>

#define     Threads     5

void* ThreadFunc(void* _args)
{
    int thread_num = *((int*)_args);

    printf("Thread %d: Hello, World!\n", thread_num);

    int* retval = new int;
    *retval = thread_num * 2;

    pthread_exit(reinterpret_cast<void*>(retval));
}

int main()
{
    pthread_t tid[Threads];

    for (int i = 0;i < Threads;i++)
    {
        int* p = new int;
        *p = i;

        int rc = pthread_create(&tid[i], nullptr, ThreadFunc, reinterpret_cast<void*>(p));
        if (rc != 0)
        {
            std::cout << "线程" << i << "创建失败" << std::endl;

            return -1;
        }
    }

    // 等待线程结束并接收返回值
    for (int i = 0;i < Threads; i++)
    {
        void* retval;

        pthread_join(tid[i], &retval);

        std::cout << "线程" << i << "退出, 返回值是" << *reinterpret_cast<int*>(retval) << std::endl;
    }

    std::cout << "线程全部退出" << std::endl;

    return 0;
}

[root@Zhn 线程]# g++ pthread_join.cpp -o pthread_join -lpthread
[root@Zhn 线程]# ./pthread_join 
Thread 0: Hello, World!
Thread 1: Hello, World!
Thread 3: Hello, World!
Thread 2: Hello, World!
Thread 4: Hello, World!
线程0退出, 返回值是0
线程1退出, 返回值是2
线程2退出, 返回值是4
线程3退出, 返回值是6
线程4退出, 返回值是8
线程全部退出
[root@Zhn 线程]# 

可以看到,程序会阻塞在pthread_join,直到所有线程全部退出。

pthread_detach

默认情况下,新创建的线程处于可连接(Joinable)的状态,可连接状态的线程退出后,需要对其执行连接操作,否则线程资源无法释放,从而造成资源泄漏。

如果其他线程并不关心线程的返回值,那么连接操作就会变成一种负担:你不需要它,但是你不去执行连接操作又会造成资源泄漏。这时候你需要的东西只是:线程退出时,系统自动将线程相关的资源释放掉,无须等待连接。NPTL提供了pthread_detach函数来将线程设置成已分离(detached)的状态,如果线程处于已分离的状态,那么线程退出时,系统将负责回收线程的资源,如下:

#include <pthread.h>

int pthread_detach(pthread_t thread);

参数就是要分离的线程ID,可以是线程组内其他线程对目标线程进行分离,也可以是线程自己执行pthread_detach函数,将自身设置成已分离的状态,如下:

pthread_detach(pthread_self())

线程的状态之中,可连接状态和已分离状态是冲突的,一个线程不能既是可连接的,又是已分离的。因此,如果线程处于已分离的状态,其他线程尝试连接线程时,会返回EINVAL错误。

返回值:

返回值说明
ESRCH传入的线程ID不存在,查无此线程
EINVAL线程不是一个可连接的线程,已经处于分离状态
// pthread_detach示例

#include <iostream>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>

void* ThreadFunc(void* _threadId)
{
    int TID = syscall(SYS_gettid);

    std::cout << "TID: " << TID << std::endl;
    std::cout << "pthread_self: " << pthread_self() << std::endl;

    pthread_exit(nullptr);
}

int main()
{
    pthread_t tid;

#ifdef USE_ATTR

    pthread_attr_t attr;

    // 将线程状态设置为分离状态
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    int rc = pthread_create(&tid, nullptr, ThreadFunc, (void*)tid);
    if (rc != 0)
    {
        std::cout << "线程创建失败" << std::endl;;

        return -1;
    }

#else

    int rc = pthread_create(&tid, nullptr, ThreadFunc, (void*)tid);
    if (rc != 0)
    {
        std::cout << "线程创建失败" << std::endl;;

        return -1;
    }

    int res = pthread_detach(tid);
    if (res != 0)
    {
        std::cout << strerror(res) << std::endl;

        return -1;
    }

#endif

    std::cout << "已设置线程分离" << std::endl;

    return 0;
}

[root@Zhn 线程]# g++ pthread_detach.cpp -o pthread_use_attr_detach -DUSE_ATTR -lpthread
[root@Zhn 线程]# g++ pthread_detach.cpp -o pthread_no_attr_detach -lpthread
[root@Zhn 线程]# ./pthread_no_attr_detach 
已设置线程分离TID: 
8428
pthread_self: pthread_self: 139697266493184 hello world
[root@Zhn 线程]# ./pthread_no_attr_detach 
已设置线程分离TID: 8485
pthread_self: 140716723963648 hello world

[root@Zhn 线程]# ./pthread_use_attr_detach 
已设置线程分离
[root@Zhn 线程]# 

我们使用两种方式设置线程分离,一种是通过属性设置,还有一种是调用pthread_detach设置分离,执行了三次程序,可以发现第三次线程没有输出,这是为什么?因为我们设置线程分离,那么主线程先抢到CPU执行之后就退出了,还没轮到子线程执行

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

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

相关文章

java之static详细总结

static也叫静态&#xff0c;可以修饰成员变量、成员方法。 成员变量 按照有无static分为两种&#xff1a; 类变量&#xff1a;static修饰&#xff0c;属于类&#xff0c;与类一起加载一次&#xff0c;在内存中只有一份&#xff0c;会被类的全部对象共享实例变量&#xff08;…

【C++ STL有序关联容器】set 集合

文章目录 【 1. 基本原理 】【 2. set 的定义 】2.1 调用默认构造函数&#xff0c;创建空的 set 容器2.2 在创建 set 容器的同时&#xff0c;对其进行初始化2.3 拷贝构造的方式创建2.4 取已有 set 容器中的部分元素&#xff0c;来初始化新 set 容器2.5 修改排序规则的方式创建 …

web安全学习笔记(6)

记一下第十节课的内容。 一.PHP语言中的if else判断 语法和c语言中非常类似&#xff0c;不再赘述&#xff0c;也可以使用if...elseif...elseif...else 1.True和False 2.&#xff0c;和 一个等号是赋值 两个等号是比较 三个等号是全等&#xff08;内容相等&#xff0c;数…

职场中的情绪管理:如何应对工作中的愤怒情绪?

在职场中&#xff0c;我们常常会遇到一些让我们感到愤怒的事情。或许是同事的不配合&#xff0c;或许是上司的不理解&#xff0c;又或许是客户的无理取闹。然而&#xff0c;作为一个职业人&#xff0c;我们需要学会如何管理自己的情绪&#xff0c;尤其是愤怒情绪&#xff0c;以…

linux文件权限与数字转化

chmod命令——change mode&#xff0c;可以对特定文件文件夹权限进行更改 这里我们看到&#xff0c;当执行了chmod u-x try.sh后&#xff0c;try文件底色变为白色&#xff0c;即为其执行权限被“减去” 在linux系统中&#xff0c;权限的减去是通过权限的数字表示来实现的&#…

elment UI el-date-picker 月份组件选定后提交后台页面显示正常,提交后台字段变成时区格式

需求&#xff1a;要实现一个日期的月份选择<el-date-picker :typeformData.dateType :value-formatdateFormat v-modelformData.leaveFactoryDateplaceholder选择月份></el-date-picker>错误示例&#xff1a;将日期显示类型(type)dateType或将日期绑定值的格式(val…

016——DHT11驱动开发(基于I.MX6uLL)

目录 一、 模块介绍 1.1 简介 1.2 电路描述 1.3 通信协议 二、 驱动程序 三、 应用程序 四、 上机实验 一、 模块介绍 1.1 简介 DHT11 是一款可测量温度和湿度的传感器。比如市面上一些空气加湿器&#xff0c;会测量空气中湿度&#xff0c;再根据测量结果决定是否继续加…

P8749 [蓝桥杯 2021 省 B] 杨辉三角形

[蓝桥杯 2021 省 B] 杨辉三角形 题目描述 下面的图形是著名的杨辉三角形: 如果我们按从上到下、从左到右的顺序把所有数排成一列&#xff0c;可以得到如下数列&#xff1a; 1 , 1 , 1 , 1 , 2 , 1 , 1 , 3 , 3 , 1 , 1 , 4 , 6 , 4 , 1 , … 1,1,1,1,2,1,1,3,3,1,1,4,6,4,1, …

LeetCode 1483.树节点的第 K 个祖先:树上倍增

【LetMeFly】1483.树节点的第 K 个祖先&#xff1a;树上倍增 力扣题目链接&#xff1a;https://leetcode.cn/problems/kth-ancestor-of-a-tree-node/ 给你一棵树&#xff0c;树上有 n 个节点&#xff0c;按从 0 到 n-1 编号。树以父节点数组的形式给出&#xff0c;其中 paren…

KeyguardClockSwitch的父类

KeyguardClockSwitch 定义在KeyguardStatusView中, mClockView findViewById(R.id.keyguard_clock_container);KeyguardClockSwitch的父类为&#xff1a; Class Name: LinearLayout Class Name: KeyguardStatusView Class Name: NotificationPanelView Class Name: Notificat…

六、从零实战企业级K8S本地部署ThingsBoard专业版集群

1、从 docker hub 拉取 ThingsBoard PE 映像(所有节点) 1.1、查看k8s信息(主节点) kubectl cluster-info #查看k8s集群信息 kubectl get node #查看节点信息 kubectl get pod -A #查看内部组件1.2、从 docker hub 拉取 ThingsBoard PE 映像(所有…

C语言进阶课程学习记录-第24课 - #pragma 使用分析

C语言进阶课程学习记录-第24课 - #pragma 使用分析 #pragma实验-#pragma messagecmd窗口运行 实验-pragma oncebcc编译报错gcc编译成功global.h代码优化 #pragma pack实验BCC编译器输出 小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程…

docker笔记(二):镜像、容器数据卷

四、 docker镜像 4.1 镜像 镜像是一种轻量级、可执行的独立软件包&#xff0c;用来打包软件运行环境和基于运行环境开发的软件&#xff0c;它包含运行某个软件所需的所有内容&#xff0c;包括代码、库、环境变量和配置文件 所有的应用&#xff0c;直接打包docker镜像就可以直…

BPMNJS 在原生HTML中的引入与使用

BPMNJS 在HTML中的引入与使用 在网上看到的大多是基于vue使用BPMN的示例或者教程&#xff0c;竟然没有在HTML使用的示例&#xff0c;有也是很简单的介绍核心库的引入和使用&#xff0c;并没有涉及到扩展库。于是简单看了下&#xff0c;真的是一波三折&#xff0c;坎坎坷坷。不…

MobTech积极参与鸿蒙生态建设,HarmonyOS NEXT鸿蒙星河版产品即将发布

1月18日&#xff0c;在鸿蒙生态千帆启航仪式上&#xff0c;华为宣布HarmonyOS NEXT鸿蒙星河版开发者预览正式面向开发者开放申请。被称为“纯血鸿蒙”的鸿蒙星河版将实现原生精致、原生易用、原生流畅、原生安全、原生智能、原生互联6大极致原生体验。 作为深耕开发者服务领域…

二分法题集1

1 二分查找 分析&#xff1a; 这是一道很简单的二分法题&#xff0c;定义两个指针和中间值middle&#xff0c;判断middle对应数组值与目标值的大小关系&#xff0c;从而对left和right进行修改。由于太过基础&#xff0c;代码简单基础就不多赘述。 目录 1 二分查找 分析&…

Oracle 数据库维的建立

Oracle 数据库维的建立 SQL> select table_name from dict where table_name like %DBA%DIM%;TABLE_NAME ------------------------------ DBA_DIMENSIONS DBA_DIM_LEVELS DBA_DIM_LEVEL_KEY DBA_DIM_ATTRIBUTES DBA_DIM_HIERARCHIES DBA_DIM_CHILD_OF DBA_DIM_JOIN_KEYsel…

专题【链表】【考试题】刷题日记

题目列表 考试题&#xff08;22题&#xff09; 2024.04.04 146. LRU 缓存 707. 设计链表 138. 随机链表的复制 160. 相交链表 622. 设计循环队列 109. 有序链表转换二叉搜索树 460. LFU 缓存 355. 设计推特 725. 分隔链表 2487. 从链表中移除节点 日常复习题 876. 链表的中…

【智能算法应用】猎人猎物优化算法(HPO)在WSN覆盖中的应用

目录 1.算法原理2.数学模型3.结果展示4.参考文献 1.算法原理 【智能算法】猎人猎物算法&#xff08;HPO&#xff09;原理及实现 2.数学模型 3.结果展示 HPO设置区域边长为20&#xff0c;节点数为35&#xff0c;感知半径为2.5&#xff0c;实验结果如下&#xff1a; 4.参考…

iMazing 3 for Windows iOS设备管理软件2024最新功能解析

iMazing 3 for Windows是一款兼容Win和Mac的iOS设备管理软件。iMazing 3 for Windows能够将音乐、文件、消息和应用等数据从任何 iPhone、iPad 或 iPod 传输到 Mac 或 PC 上。 iMazing 3 win 版下载&#xff1a; https://souurl.cn/Qp6gFU iMazing 3 mac 版下载&#x…