Linux下的多线程编程:原理、工具及应用(4)

                                               🎬慕斯主页修仙—别有洞天

                                              ♈️今日夜电波:Flower of Life—陽花

                                                                0:34━━━━━━️💟──────── 4:46
                                                                    🔄   ◀️   ⏸   ▶️    ☰  

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


目录

生产者消费者模型为什么高效?

信号量再理解

信号量的概念

信号量的接口

sem_t

sem_init()

sem_post()

sem_wait()

sem_trywait()

sem_destroy()

通过基于环形队列的生产消费模型理解信号量

先申请锁还是先申请信号量的问题

环形队列与阻塞队列的区别


生产者消费者模型为什么高效?

        生产者与消费者模型之所以高效,是因为它通过以下机制实现了数据的高效处理和线程间的有效协作:

  1. 解耦生产与消费过程:生产者-消费者模式将数据的生产和消费过程分离,允许生产者和消费者独立地工作,从而避免了两者之间的直接依赖。这种解耦可以减少系统各部分之间的等待时间,提高整体效率。
  2. 缓冲队列:在生产者和消费者之间通常存在一个缓冲队列,生产者将数据放入队列,而消费者从队列中取出数据进行处理。这个队列作为中介,平衡了生产和消费的速率差异。
  3. 并发控制:使用互斥锁和条件变量等同步机制来控制对共享资源的访问,确保生产者在消费者处理完数据后再添加新数据,避免了数据竞争和一致性问题。
  4. 减少等待时间:当缓冲区满时,生产者会等待,直到消费者消费了一些数据;当缓冲区空时,消费者会等待,直到生产者生产了一些数据。这种等待机制使得生产者和消费者不会在没有工作可做时占用CPU资源。
  5. 无锁设计:在某些高效的实现中,如Disruptor框架,采用了无锁的设计,通过环形队列等技术减少了线程间同步的开销,进一步提高了性能。
  6. 任务并行化:生产者-消费者模型允许多个生产者和消费者同时工作,只要它们遵守同步规则,就可以实现任务的并行化,从而提高系统的吞吐量。
  7. 灵活性和扩展性:该模型提供了一种灵活的方式来处理不同类型的数据和任务,可以根据需要调整生产者和消费者的数量,以及缓冲区的大小,以适应不同的工作负载和性能要求。

        综上所述,生产者与消费者模型之所以高效,是因为其能够有效地协调生产者和消费者之间的工作,减少不必要的等待,提高资源的利用率,同时通过并发控制机制保证数据的安全和一致性,这些因素共同作用使得该模型在多线程和并发编程中非常高效。

信号量再理解

信号量的概念

        信号量(Semaphore)是一种同步机制,用于控制对共享资源的访问

        信号量的核心概念是它作为一个计数器,用于代表可用资源的数量。信号量的值如果大于0,表示有资源可供使用;如果等于0,则表示没有可用资源,此时试图访问资源的线程或进程将被阻塞,直到资源再次变得可用。

        信号量的操作通常包括P操作和V操作。注意:PV操作是原子的!P操作用于请求资源,当信号量的值大于0时,执行P操作的线程将减少信号量的值,并继续执行;如果信号量的值为0,则线程将等待,直到信号量的值变为正数。V操作用于释放资源,执行V操作的线程将增加信号量的值,如果有其他线程在等待该信号量,那么其中一个将被唤醒并获得对资源的访问权限。申请信号的本质就是预定资源。

        总的来说,信号量是操作系统中用于解决并发问题的一种重要工具,它通过协调不同线程或进程对共享资源的访问,避免了竞态条件的发生,确保了系统的稳定性和数据的一致性

信号量的接口

sem_t

        Linux中sem_t是一个用于同步进程的信号量数据类型,它本质上是一个结构体。

        以下是关于Linux中sem_t的详细说明:

  1. 定义和初始化
  • sem_t是一个结构体类型,通常包含一个长整型数值,用于表示信号量的当前值。
  • 使用sem_init()函数来初始化一个sem_t类型的信号量。该函数接受三个参数:一个指向sem_t结构的指针,一个表示共享属性的整数,以及一个无符号整数作为信号量的初始值。
  1. 共享属性
  • sem_init()函数的第二个参数pshared为0时,信号量只能被当前进程的所有线程共享。
  • 如果pshared不为0,则信号量可以在多个进程之间共享,这要求信号量具有系统范围的持久性。
  1. 操作函数
  • sem_post():用于增加信号量的值,通常在释放资源时调用。
  • sem_wait():用于减少信号量的值,如果信号量为0,则调用此函数的线程将被阻塞,直到信号量变为正数。
  • sem_trywait():尝试减少信号量的值,如果不为0,则立即返回;如果为0,则立即返回错误,不会阻塞线程。
  • sem_destroy():用于销毁一个信号量,释放它占用的资源。
  1. 用途:信号量通常用于控制对共享资源的访问,确保在任何时刻只有一个线程或进程可以访问资源。它们在多线程编程中非常重要,可以帮助避免条件竞争和数据不一致的问题。

sem_init()

    sem_init函数是POSIX线程库中的一个用于初始化信号量的函数。它用于设置信号量的初始状态,包括其初始值以及确定它是被同一进程内的多个线程共享还是可以被多个进程共享。

        以下是关于sem_init函数的详细说明:

  1. 函数原型
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
  1. 参数说明
  • sem: 这是一个指向要初始化的信号量变量的指针。
  • pshared: 这个整型参数指定了信号量的共享类型。如果它的值为0,则表示这个信号量是进程内共享的,只能被初始化它的进程内的线程使用。如果pshared的值不为0,则表示信号量可以在多个进程之间共享。
  • value: 这个无符号整型参数设置了信号量的初始值。信号量的初始值通常代表可用资源的数量。
  1. 共享属性
  • 如果pshared设置为0,信号量应放置在进程的所有线程都可见的地址上,例如全局变量或堆上动态分配的变量。
  • 如果pshared非零,则应将信号量置于共享内存区域,以允许不同进程间的访问。这可能涉及使用诸如shm_open()mmap()shmget()之类的函数来创建或获取共享内存区域。
  1. 返回值与错误
  • sem_init函数在成功时返回0。如果出现错误(如无效的参数、没有足够空间来创建信号量等),它将返回-1,并通过errno设置合适的错误代码。

sem_post()

    sem_post()函数在多线程编程中扮演着重要角色,其主要用途是释放信号量,允许其他等待该信号量的线程继续执行

        以下是关于sem_post()函数的详细说明:

  1. 函数原型
#include <semaphore.h>
int sem_post(sem_t *sem);
  1. 功能描述
  • sem_post()函数通过原子操作将信号量的值增加1,这通常对应于释放资源或通知其他正在等待该资源的线程可以继续执行。
  • 它与sem_wait()函数配合使用,以控制对共享资源的访问。当一个线程完成了对共享资源的使用后,它会调用sem_post()来增加信号量的值,从而允许其他等待这个资源的线程获得访问权限。
  • 信号量的值如果为正,则表示有相应数量的资源可供使用;如果值为0,则表示资源被占用,其他试图获取资源的线程将不得不等待。
  1. 参数说明
  • sem:这是一个指向已初始化的信号量变量的指针,该信号量由sem_init()函数初始化。
  1. 返回值
  • sem_post()在成功时返回0。如果出现错误(例如,如果信号量未被正确初始化),它将返回-1。
  1. 使用场景
  • 假设有一个计数器作为共享资源,多个线程需要对其进行增加操作。在这种情况下,可以使用sem_post()sem_wait()确保每次只有一个线程能够修改计数器的值,避免竞态条件的发生。

sem_wait()

    sem_wait()是一个同步原语,用于在多线程环境中等待信号量变为非零值。其主要目的是确保在多个线程或进程间安全地访问共享资源。

        以下是其详细解释:

  1. 函数原型
#include <semaphore.h>
int sem_wait(sem_t *sem);
  1. 功能描述
  • sem_wait()函数通过原子操作将信号量的值减1。如果信号量的值为正,则立即减少并返回;如果信号量值为0,调用此函数的线程会阻塞,直到信号量值大于0为止。
  • 该函数保证了在任一时刻只有一个线程能够进入临界区,从而防止了同时对共享资源的访问造成的竞态条件。
  1. 参数说明
  • sem:指向已由sem_init初始化过的sem_t类型信号量变量的指针。
  1. 行为细节
  • 如果信号量的值大于0,sem_wait()将其减1,并立即返回,允许线程继续执行。
  • 如果信号量的值为0,调用sem_wait()的线程将进入睡眠状态,直到其他线程通过sem_post()增加信号量的值,使其不再为0。此时,sem_wait()将减少信号量的值,并唤醒等待的线程之一,让其继续执行。
  1. 错误处理
  • 成功时,sem_wait()返回0。如果出现错误(例如信号量未被正确初始化),它将返回-1,并设置errno以指示具体的错误原因。

sem_trywait()

    sem_trywait()是一个同步原语,用于在多线程环境中等待信号量变为非零值。与sem_wait()不同之处在于,sem_trywait()不会阻塞调用它的线程,而是立即返回一个错误码。

        以下是其详细解释:

  1. 函数原型
#include <semaphore.h>
int sem_trywait(sem_t *sem);
  1. 功能描述
  • sem_trywait()函数通过原子操作将信号量的值减1。如果信号量的值为正,则立即减少并返回;如果信号量值为0,调用此函数的线程不会阻塞,而是立即返回一个错误码。
  • 该函数保证了在任一时刻只有一个线程能够进入临界区,从而防止了同时对共享资源的访问造成的竞态条件。
  1. 参数说明
  • sem:指向已由sem_init初始化过的sem_t类型信号量变量的指针。
  1. 行为细节
  • 如果信号量的值大于0,sem_trywait()将其减1,并立即返回0,允许线程继续执行。
  • 如果信号量的值为0,调用sem_trywait()的线程将立即返回-1,表示无法获取到信号量。
  1. 错误处理
  • 成功时,sem_trywait()返回0。如果信号量的值为0,它将返回-1。

sem_destroy()

    sem_destroy()是用于销毁信号量的函数,它在多线程编程中用于同步控制。

        以下是关于sem_destroy()函数的详细说明:

  1. 函数原型
#include <semaphore.h>
int sem_destroy(sem_t *sem);
  1. 功能描述
  • sem_destroy()函数用于销毁一个已经初始化的信号量。这个操作会释放与信号量相关的所有资源,使得该信号量不再可用。
  • 在多线程环境中,当一个信号量不再需要时,应当使用sem_destroy()来销毁它,以避免资源泄露。
  1. 参数说明
  • sem:这是一个指向已初始化的信号量变量的指针,该信号量由sem_init()sem_open()初始化。
  1. 返回值
  • sem_destroy()在成功时返回0。如果出现错误(例如,如果指定的信号量未被正确初始化),它将返回-1。
  1. 使用场景
  • 在多线程程序中,当不再需要一个信号量时,应当调用sem_destroy()来释放相关资源。这通常在程序的清理阶段或者在确定信号量不再被使用时进行。
  1. 注意事项
  • 在销毁一个信号量之前,确保没有线程正在等待或持有该信号量,否则可能会导致未定义的行为。
  • 对于有名信号量(通过sem_open()创建的),应当使用sem_close()来关闭它,然后使用sem_unlink()来删除它。

通过基于环形队列的生产消费模型理解信号量

        生产消费模型是一种常见的并发编程模型,用于解决生产者和消费者之间的协同工作问题。在这种模型中,生产者负责生产数据,消费者负责消费数据。环形队列是实现生产消费模型的一种常用数据结构。

        基于环形队列的生产消费模型可以描述如下:

  1. 创建一个固定大小的环形队列,用于存储生产者生产的数据。
  2. 生产者不断地向队列中添加数据,如果队列已满,则等待直到有空闲位置。
  3. 消费者不断地从队列中取出数据进行处理,如果队列为空,则等待直到有新的数据可用。
  4. 通过信号量或条件变量等同步机制,实现生产者和消费者之间的协调和互斥访问队列。

        ​如下:我们通过两个信号量创建了基于环形队列的生产消费模型,这里的重点在Push和pop操作的理解:我们使用P和V封装了sem_wait和sem_post,分别代表信号量的减少以及增加。我们定义了两个信号量,分别是生产者(对于空间而言)的信号量、消费者(对于数据而言)的信号量需要注意的是:我们在Push中是先对于生产者的信号量(也就是空间)减一,在执行完成任务的入队后再对消费者的信号量(也就是数据)+1,这就表示了生产完成了一个任务,也是给消费者一个信号,如果消费者不能消费了(没有数据了),通过这个信号就可以进行消费。消费者也是相似,先是对消费者信号量减一,出队操作,在让生产者的信号加一,表示消费完成,也是个生产者一个信号,如果生产者不能生产了(空间满了),通过这个信号可以进行生产这两个过程让生产与消费可以同时的进行,让生产与消费间进行了解耦,大大的提高了运行的效率!

#pragma once#include <iostream>
#include <vector>
#include <semaphore.h>
#include "LockGuard.hpp"const int defaultsize = 5;template <class T>
class RingQueue
{
private:void P(sem_t &sem){sem_wait(&sem);}void V(sem_t &sem){sem_post(&sem);}public:RingQueue(int size = defaultsize): _ringqueue(size), _size(size), _p_step(0), _c_step(0){sem_init(&_space_sem, 0, size);sem_init(&_data_sem, 0, 0);pthread_mutex_init(&_p_mutex, nullptr);pthread_mutex_init(&_c_mutex, nullptr);}void Push(const T &in){P(_space_sem);_ringqueue[_p_step++] = in;_p_step %= _size;V(_data_sem);}void Pop(T *out){// 消费P(_data_sem);*out = _ringqueue[_c_step++];_c_step %= _size;V(_space_sem);}~RingQueue(){sem_destroy(&_space_sem);sem_destroy(&_data_sem);pthread_mutex_destroy(&_p_mutex);pthread_mutex_destroy(&_c_mutex);}private:std::vector<T> _ringqueue;int _size;int _p_step; // 生产者的生产位置int _c_step; // 消费位置sem_t _space_sem; // 生产者sem_t _data_sem;  // 消费者};

需要注意的是:上述代码只能运行一个生产者以及消费者一同进行生产消费,因为如果多个线程同时访问临界区资源可能会导致数据竞争和不一致的结果,因此我们需要使用互斥锁来解决这个问题,对此,我们基于上述代码增加两个互斥锁,分别用于Push和Pop:

#pragma once#include <iostream>
#include <vector>
#include <semaphore.h>
#include "LockGuard.hpp"const int defaultsize = 5;template <class T>
class RingQueue
{
private:void P(sem_t &sem){sem_wait(&sem);}void V(sem_t &sem){sem_post(&sem);}public:RingQueue(int size = defaultsize): _ringqueue(size), _size(size), _p_step(0), _c_step(0){sem_init(&_space_sem, 0, size);sem_init(&_data_sem, 0, 0);pthread_mutex_init(&_p_mutex, nullptr);pthread_mutex_init(&_c_mutex, nullptr);}// void Push(const T &in)// {//     P(_space_sem);//     _ringqueue[_p_step++] = in;//     _p_step %= _size;//     V(_data_sem);// }void Push(const T &in){// 生产// 先加锁1,还是先申请信号量?2P(_space_sem);{LockGuard lockGuard(&_p_mutex);_ringqueue[_p_step] = in;_p_step++;_p_step %= _size;}V(_data_sem);}// void Pop(T *out)// {//     // 消费//     P(_data_sem);//     *out = _ringqueue[_c_step++];//     _c_step %= _size;//     V(_space_sem);// }void Pop(T *out){// 消费P(_data_sem);{LockGuard lockGuard(&_c_mutex);*out = _ringqueue[_c_step];_c_step++;_c_step %= _size;}V(_space_sem);}~RingQueue(){sem_destroy(&_space_sem);sem_destroy(&_data_sem);pthread_mutex_destroy(&_p_mutex);pthread_mutex_destroy(&_c_mutex);}private:std::vector<T> _ringqueue;int _size;int _p_step; // 生产者的生产位置int _c_step; // 消费位置sem_t _space_sem; // 生产者sem_t _data_sem;  // 消费者pthread_mutex_t _p_mutex;pthread_mutex_t _c_mutex;
};

先申请锁还是先申请信号量的问题

        在并发编程中,关于先申请锁还是先申请信号量的问题,实际上取决于具体的场景和需求。这两者都是用于控制多线程访问共享资源的机制,但它们的作用和使用方式有所不同。

        锁(Lock)主要用于保证同一时间只有一个线程可以访问特定的代码段或资源,这是通过互斥(Mutual Exclusion)来实现的。当一个线程获得锁时,其他试图进入临界区的线程将被阻塞,直到锁被释放。这种机制确保了数据的一致性和完整性,防止了数据竞争(Data Race)的发生。

        信号量(Semaphore)则是一种更通用的同步原语,它可以用来控制多个线程对共享资源的访问。信号量有一个计数值,表示可用资源的数量。当线程需要访问资源时,它会尝试减少信号量的计数值;如果计数值为0,则线程将被阻塞。当其他线程释放资源并增加信号量的计数值时,被阻塞的线程可能会被唤醒并继续执行。

        在某些情况下,你可能需要先申请锁,以确保在访问共享资源时不会受到其他线程的干扰。然后,在访问完资源后,你可以释放锁并申请信号量,以控制其他线程对资源的访问。这种情况下,锁用于保证互斥,而信号量用于同步和资源管理。

        然而,在其他情况下,你可能需要先申请信号量,以确保有足够的资源可供当前线程使用。然后,在访问资源之前,你可以申请锁以保证互斥。这种情况下,信号量用于控制资源的分配和使用,而锁用于确保在访问资源时的互斥性。

        因此,先申请锁还是先申请信号量并没有一个固定的答案。它取决于你的具体需求、资源的使用情况以及你对并发控制的要求。在设计并发程序时,你需要仔细考虑这些因素,并选择最适合你的场景的同步机制。

环形队列与阻塞队列的区别

        首先,从数据结构的角度来看,环形队列,也称为循环队列,其内部实现通常基于一个固定大小的数组。它利用两个指针(队头和队尾)来追踪队列中的元素,当队尾指针到达数组的末尾时,它会回绕到数组的起始位置,从而形成一个“环形”。这种设计使得环形队列能够高效地进行元素的入队和出队操作,并且在空间使用上更为紧凑

        而阻塞队列则是一种支持阻塞操作的队列。它可能基于链表、数组或其他数据结构实现。阻塞队列的关键特性在于,当队列为空时,尝试从队列中获取元素的线程会被阻塞,直到队列中有新元素可用;当队列已满时,尝试向队列中添加元素的线程同样会被阻塞,直到队列中有空闲空间。这种阻塞机制使得阻塞队列在多线程环境中能够自然地协调生产者和消费者的速度差异。

        在功能特性方面,环形队列主要关注于高效的空间利用和快速的入队出队操作。它通常适用于固定大小的缓存、循环缓冲区等场景。由于环形队列的大小是固定的,因此它不适合用于需要动态调整容量的场景。

        而阻塞队列则更注重于线程间的同步和协调。它通过阻塞操作来确保生产者和消费者之间的平衡,避免了数据的丢失或过度生产。阻塞队列在多线程编程中广泛应用,尤其是在需要处理大量数据且生产者和消费者速度不一致的场景中

        接下来,我们来看使用场景方面的差异。环形队列通常用于那些需要固定大小缓冲区的场景,例如网络通信中的数据包缓存、音频或视频流的处理等。在这些场景中,数据的产生和消费速度相对稳定,且数据量的大小可以预先确定。

        而阻塞队列则更适用于那些生产者和消费者速度不一致的场景。例如,在文件读取或网络请求等I/O密集型任务中,生产者线程可能会因为等待I/O操作而暂时停止产生数据,而消费者线程则可能持续地从队列中获取数据并处理。此时,阻塞队列可以有效地协调两者的速度差异,避免数据的丢失或过度生产。

        以实际例子来说,假设我们有一个处理网络请求的应用,其中生产者线程负责接收网络数据并将其放入队列,而消费者线程则从队列中取出数据并处理。如果采用环形队列,我们需要确保生产者线程不会过快地产生数据导致队列溢出;而如果采用阻塞队列,即使生产者线程的速度突然变慢或停止产生数据,消费者线程也可以安全地从队列中获取数据(在队列为空时阻塞),从而避免了数据的丢失。


                       感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       

                                                                        给个三连再走嘛~  

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

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

相关文章

RuoYi-Vue开源项目2-前端登录验证码生成过程分析

前端登录验证码实现过程 生成过程分析 生成过程分析 验证码的生成过程简单概括为&#xff1a;前端登录页面加载时&#xff0c;向后端发送一个请求&#xff0c;返回验证码图片给前端页面展示 前端页面加载触发代码&#xff1a; import { getCodeImg } from "/api/login&q…

Spring6--基础概念

1. 概述 1.1. Spring是什么 Spring 是一套广泛应用于 Java 企业级应用开发领域的轻量级开源框架&#xff0c;由 Rod Johnson 创立&#xff0c;旨在显著降低 Java 企业应用的复杂性&#xff0c;缩短开发周期&#xff0c;并提升开发效率。Spring 不仅适用于服务器端开发&#x…

三个案例,带你看懂智能时代支撑降本增效的底层逻辑

2003年&#xff0c;“神舟五号”成功登上太空&#xff0c;2007年&#xff0c;乔布斯初代苹果发布会&#xff0c;2016年“天宫二号”与“神州十一号”自动交会对接成功&#xff0c;2022年ChatGPT横空出市。 科技发展速度令人惊叹&#xff0c;一不留神就步入了下一个科技时代&am…

【vue elementUI】el-select和弹出框el-option样式调整,::v-deep失效

组件自带样式&#xff1a; 修改后样式&#xff1a; 注意修改弹出框样式需要修改一个属性&#xff1a; 此属性默认值为true&#xff0c;此时可以看到弹出框是放在外面的&#xff0c;没有在el-select里面。此时设置弹窗样式会不生效&#xff0c;::v-deep无效。 需要将此属性改为f…

JavaScript练手小技巧:数字反转时钟

样式基于博主的这篇文章&#xff1a; CSS3技巧38&#xff1a;3D 翻转数字效果-CSDN博客 既然可以实现翻转数字了&#xff0c;肯定就可以跟 JS 相结合去完成一些数字展示效果。 比如&#xff0c;数字反转时钟。 为了方便&#xff0c;所有 HTML 数字根据时间动态生成。因此&a…

vue3 element plus 上传下载

文章目录 上传下载 上传 /* html */ <el-upload v-model"fileId" class"avatar-uploader" ref"exampleUploadRef" :file-list"fileList" :show-file-list"false" action"/ys-three-year/ThreeReport/uploadFile&q…

Coarse-to-Fine Latent Diffusion for Pose-Guided Person Image Synthesis阅读笔记

连更&#xff01;&#xff01; 0 Abstract 先前的姿势引导图像合成方法简单的将人的外观与目标姿势进行对齐&#xff0c;这容易导致过拟合&#xff0c;因为缺乏对source person image的high-level semantic understanding&#xff1b;文章开发了一种新的训练范式&#xff1a;…

mudo服务器测试一

目录 长连接测试 测试代码 客户端 服务端 超时连接测试 测试代码 客户端 服务端 错误请求测试 测试代码 场景一 客户端 服务端 场景二 客户端 服务端 长连接测试 测试代码 /*长连接测试1: 创建一个客户端持续给服务器发送数据,直到超过时间看是否正常*/ #inc…

实用工具推荐----geek 卸载软件的神器

Geek Uninstaller 是一款软件卸载工具。它提供简单易用的界面和强大的卸载功能&#xff0c;能快速扫描和识别应用程序&#xff0c;并彻底删除与之相关的文件和注册表项&#xff0c;确保完全清除应用程序。它还可以监视应用程序安装过程&#xff0c;并记录创建的文件和注册表项…

计算机组成原理练习-计算机性能指标

CPU时间与IO时间 ------------------------------------------------------------------------------------------------------------------------------- 1.假定基准程序A在某计算机上的运行时间为100秒&#xff0c;其中90秒为CPU时间&#xff0c;其余 为l/O时间。若CPU速度…

WebGIS之实现查询地区天气并让地区高亮

一.预览>> 二.思路>> 根据搜索框的内容来进行页面视角的切换&#xff0c;对应的地区高亮&#xff0c;右边有关天气的地方实时更新&#xff0c;并且因为代码体量非常小&#xff0c;并没有选择在框架下完成。直接一个html文件搞定了&#xff0c;但实际上还是有一些坑…

带有超令牌采样的视觉转换器

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 摘要Abstract文献阅读&#xff1a;带有超令牌采样的视觉转换器1、研究背景2、方法提出3、优势4、实验5、贡献 二、StokenAttention代码学习 摘要 本周主要阅读了CV…

Elasticsearch数据存储优化方案

优化Elasticsearch数据存储有助于提升系统性能、降低成本、提高数据查询效率以及增强系统的稳定性和可靠性。通常我们再优化Elasticsearch数据存储会遇到一些问题&#xff0c;导致项目卡壳。以下是优化Elasticsearch数据存储的一些重要作用&#xff1a; 1、问题背景 在某些场景…

AI系统性学习03—ChatGPT开发教程

文章目录 1、OpenAI关键概念⭐️2、OpenAI SDK介绍3、OpenAI API KEY&API 认证3.1 REST API安全认证 4、OpenAI模型⭐️4.1 模型分类4.2 GPT44.3 GPT-3.54.4 Embeddings 5、OpenAI快速入门6、Function calling(函数调用)⭐️⭐️⭐️6.1 应用场景6.2 支持function calling的…

打破传统,拥抱未来:解锁企业数字化转型成功的11把金钥匙

数字化转型是一个持续的过程&#xff0c;需要企业不断地适应新技术和市场变化。企业如何提高转型成功的可能性&#xff0c;并在竞争激烈的市场中保持领先地位。今天我们来解锁企业数字化转型成功的11把金钥匙。 清晰的战略目标&#xff1a; 首先&#xff0c;企业需要明确数字化…

如何高效接入 Flink: Connecter / Catalog API 核心设计与社区进展

本文整理自阿里云实时计算团队 Apache Flink Committer 和 PMC Member 任庆盛在 FFA 2023 核心技术专场&#xff08;二&#xff09;中的分享&#xff0c;内容主要分为以下四部分&#xff1a; Source APISink API将 Connecter 集成至 Table /SQL APICatalog API 在正式介绍这些 …

CornerStone之读取txt文件点数据

1. 页面标签 页面中目前只提供一个按钮来进行输入文件 <input click"importZeroOne" type"file" />2. 函数定义 在输入文件之后&#xff0c;执行importZeroOne函数&#xff0c;获得输入的文件&#xff0c;进行以下处理 const importZeroOne((eve…

Vue-Vuex

文章目录 vuex是什么什么时候使用Vuex原理Vuex环境搭建实例操作 vuex是什么 1.专门在vue中实现集中式状态&#xff08;数据&#xff09;管理的一个vue插件&#xff0c;对vue应用中多个组件的共享状态进行集中式的管理&#xff08;读/写&#xff09;&#xff0c;也是一种组件之…

【Django框架学习笔记】超详细的Python后端开发Django框架学习笔记

十二&#xff0c;Django框架 可以以下链接获取Django框架学习笔记,md文档和pdf文档 Django框架超详细的学习笔记&#xff0c;点击我获取 12.1 命令行操作 # 创建django项目 django-admin startproject aini# 启动项目 cd /mysite python3 manage.py runserver## 创建应用 …

23双体系Java学习之字符串的常用操作和==,equals的区别

字符串的常用操作 toCharArray的基本用法&#xff0c;将字符串转变成数组 String str "Hello, World!"; char[] charArray str.toCharArray(); charAt的基本用法 &#xff0c;接受一个整数索引作为参数&#xff0c;并返回该索引位置的字符。 String str "…