【LinuxC】C语言线程(pthread)

文章目录

  • 一、 POSIX 线程库
    • 1.1 POSIX标准
    • 1.2 Pthreads
    • 1.2 数据类型、函数、宏
      • 1.21 数据类型
      • 1.22 函数
      • 1.23 宏
  • 二、创建线程
  • 三、线程同步
  • 四、线程销毁
  • 五、示例
    • 5.1 完整示例
    • 5.2 信号量示例

本专栏上一篇文章是Windows下(MSVC)的线程编程,需要的自行查看。
【C语言】Windows下的C语言线程编程详解

进程:进程是操作系统分配资源的基本单位,每个进程有独立的地址空间,进程间通信需要通过特定的机制。
线程:线程是进程中的一个执行单元,它们共享进程的资源。线程之间的切换比进程之间的切换代价小,因此线程编程可以提高程序的并发性能。
并发与并行:并发是指多个任务在同一时间段内交替执行,而并行是指多个任务在同一时刻同时执行。线程编程可以实现并发执行,但是否能够实现并行执行取决于系统的处理器数量和调度策略。

一、 POSIX 线程库

1.1 POSIX标准

POSIX标准是一系列确保操作系统之间兼容性的规范,旨在提升软件的可移植性

POSIX,全称为Portable Operating System Interface,即“可移植操作系统接口”,是由IEEE计算机协会制定的标准。它的主要目的是定义一套操作系统应该遵循的规则和接口,以保证在不同的操作系统之间能够运行相同的程序,而无需或只需很少的修改

POSIX标准涵盖了很多方面,包括系统调用、命令行 shell、文件系统接口、线程等。这些标准允许开发者编写的程序能够在支持POSIX的任何操作系统上运行,从而提高了软件的可移植性和互操作性。

总的来说,POSIX标准是Unix和类Unix系统(如Linux、MacOS)设计的核心部分,它通过提供一致的编程接口,帮助确保应用程序的可移植性和兼容性。

注意标准和实现的区别。POSIX标准主要由C语言实现,而C语言标准则主要关注语言本身的特性。

1.2 Pthreads

POSIX线程(英语:POSIX Threads,常被缩写为pthreads)是POSIX的线程标准,定义了创建和操纵线程的一套API。

实现POSIX线程标准的库常被称作pthreads,一般用于Unix-like POSIX系统,如Linux、 Solaris。但是Microsoft Windows上的实现也存在,例如直接使用Windows API实现的第三方库pthreads-w32;而利用Windows的SFU/SUA子系统,则可以使用微软提供的一部分原生POSIX API。

Pthreads定义了一套C语言的类型、函数与常量,它以pthread.h头文件和一个线程库实现。

Pthreads API中大致共有100个函数调用,全都以"pthread_"开头,并可以分为四类:

  • 线程管理,例如创建线程,等待(join)线程,查询线程状态等。
  • 互斥锁(Mutex):创建、摧毁、锁定、解锁、设置属性等操作
  • 条件变量(Condition Variable):创建、摧毁、等待、通知、设置与查询属性等操作
  • 使用了互斥锁的线程间的同步管理

POSIX的Semaphore API(包含在semaphore.h中)可以和Pthreads协同工作,但这并不是Pthreads的标准。因而这部分API是以"sem_“打头,而非"pthread_”。

1.2 数据类型、函数、宏

1.21 数据类型

  • pthread_t:线程句柄(Windows叫句柄,Linux叫标识符,知道即可)。它是一个结构体数据类型,用于唯一标识一个线程。出于移植目的,不能把它作为整数处理,应使用函数pthread_equal()对两个线程ID进行比较。获取自身所在线程id使用函数pthread_self()。

  • pthread_attr_t:线程属性。主要包括scope属性、detach属性、堆栈地址、堆栈大小、优先级。主要属性的意义如下:

    • __detachstate,表示新线程是否与进程中其他线程脱离同步。如果设置为PTHREAD_CREATE_DETACHED,则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。可以在线程创建并运行以后用pthread_detach()来设置。一旦设置为PTHREAD_CREATE_DETACHED状态,不论是创建时设置还是运行时设置,则不能再恢复到PTHREAD_CREATE_JOINABLE状态。
    • __schedpolicy,表示新线程的调度策略,包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过pthread_setschedparam()来改变。
    • __schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。系统支持的最大和最小的优先级值可以用函数sched_get_priority_max和sched_get_priority_min得到。
    • __inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。
    • __scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
  • pthread_mutex_t:互斥锁的类型,用于保护共享资源免受多个线程的同时访问。

  • pthread_cond_t:条件变量的类型,用于线程间的同步,允许线程等待某个特定条件的发生。。

  • pthread_rwlock_t:读写锁的类型,允许多个线程同时读取共享资源,但只允许一个线程写入。

1.22 函数

线程操纵函数(简介起见,省略参数):

  • pthread_create():创建一个线程
  • pthread_exit():终止当前线程
  • pthread_cancel():请求中断另外一个线程的运行。被请求中断的线程会继续运行,直至到达某个取消点(Cancellation Point)。取消点是线程检查是否被取消并按照请求进行动作的一个位置。POSIX 的取消类型(Cancellation Type)有两种,一种是延迟取消(PTHREAD_CANCEL_DEFERRED),这是系统默认的取消类型,即在线程到达取消点之前,不会出现真正的取消;另外一种是异步取消(PHREAD_CANCEL_ASYNCHRONOUS),使用异步取消时,线程可以在任意时间取消。系统调用的取消点实际上是函数中取消类型被修改为异步取消至修改回延迟取消的时间段。几乎可以使线程挂起的库函数都会响应CANCEL信号,终止线程,包括sleep、delay等延时函数。
  • pthread_join():阻塞当前的线程,直到另外一个线程运行结束
  • pthread_kill():向指定ID的线程发送一个信号,如果线程不处理该信号,则按照信号默认的行为作用于整个进程。信号值0为保留信号,作用是根据函数的返回值判断线程是不是还活着。
  • pthread_cleanup_push():线程可以安排异常退出时需要调用的函数,这样的函数称为线程清理程序,线程可以创建多个清理程序。线程清理程序的入口地址使用栈保存,实行先进后处理原则。由pthread_cancel或pthread_exit引起的线程结束,会次序执行由pthread_cleanup_push压入的函数。线程函数执行return语句返回不会引起线程清理程序被执行。
  • pthread_cleanup_pop():以非0参数调用时,引起当前被弹出的线程清理程序执行。
  • pthread_setcancelstate():允许或禁止取消另外一个线程的运行。
  • pthread_setcanceltype():设置线程的取消类型为延迟取消或异步取消。

线程属性函数:

  • pthread_attr_init():初始化线程属性变量。运行后,pthread_attr_t结构所包含的内容是操作系统支持的线程的所有属性的默认值。
  • pthread_attr_setdetachstate():设置线程属性变量的detachstate属性(决定线程在终止时是否可以被joinable)
  • pthread_attr_getdetachstate():获取脱离状态的属性
  • pthread_attr_setscope():设置线程属性变量的__scope属性
  • pthread_attr_setschedparam():设置线程属性变量的schedparam属性,即调用的优先级。
  • pthread_attr_getschedparam():获取线程属性变量的schedparam属性,即调用的优先级。
  • pthread_attr_destroy():删除线程的属性,用无效值覆盖

互斥锁函数:

  • pthread_mutex_init() 初始化互斥锁
  • pthread_mutex_destroy() 删除互斥锁
  • pthread_mutex_lock():占有互斥锁(阻塞操作)
  • pthread_mutex_trylock():试图占有互斥锁(不阻塞操作)。即,当互斥锁空闲时,将占有该锁;否则,立即返回。
  • pthread_mutex_unlock(): 释放互斥锁
  • pthread_mutexattr_(): 互斥锁属性相关的函数

条件变量函数:

  • pthread_cond_init():初始化条件变量
  • pthread_cond_destroy():销毁条件变量
  • pthread_cond_signal(): 发送一个信号给正在当前条件变量的线程队列中处于阻塞等待状态的线程,使其脱离阻塞状态,唤醒后继续执行。如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。一般只给一个阻塞状态的线程发信号。假如有多个线程正在阻塞等待当前条件变量,则根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但pthread_cond_signal在多处理器上可能同时唤醒多个线程,当只能让一个被唤醒的线程处理某个任务时,其它被唤醒的线程就需要继续wait。POSIX规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,有些实现为了简便,在单处理器上也会唤醒多个线程。所以最好对pthread_cond_wait()使用while循环对条件变量是否满足做条件判断。
  • pthread_cond_wait(): 等待条件变量的特殊条件发生;pthread_cond_wait() 必须与一个pthread_mutex配套使用。该函数调用实际上依次做了3件事:对当前pthread_mutex解锁、把当前线程挂起到当前条件变量的线程队列、被其它线程的信号唤醒后对当前pthread_mutex申请加锁。如果线程收到一个信号被唤醒,将被配套的互斥锁重新锁住,pthread_cond_wait() 函数将不返回直到线程获得配套的互斥锁。需要注意的是,一个条件变量不应该与多个互斥锁配套使用。
  • pthread_cond_broadcast(): 某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.
  • pthread_condattr_(): 条件变量属性相关的函数

线程私有存储(Thread-local storage):

  • pthread_key_create(): 分配用于标识进程中线程特定数据的pthread_key_t类型的键
  • pthread_key_delete(): 销毁现有线程特定数据键
  • pthread_setspecific(): 为指定线程的特定数据键设置绑定的值
  • pthread_getspecific(): 获取调用线程的键绑定值,并将该绑定存储在 value 指向的位置中

同步屏障函数

  • pthread_barrier_init(): 同步屏障初始化
  • pthread_barrier_wait():
  • pthread_barrier_destory():

其它多线程同步函数:

  • pthread_rwlock_*(): 读写锁

工具函数:

  • pthread_equal(): 对两个线程的线程标识号进行比较
  • pthread_detach(): 分离线程
  • pthread_self(): 查询线程自身线程标识号
  • pthread_once(): 某些需要仅执行一次的函数。其中第一个参数为pthread_once_t类型,是内部实现的互斥锁,保证在程序全局仅执行一次。

信号量函数,包含在semaphore.h中:

  • sem_init:用于初始化一个无名信号量。它需要三个参数:一个指向信号量对象的指针、一个表示信号量是否在多个进程间共享的标志,以及信号量的初始值。
  • sem_post:用于增加信号量的值,通常在释放资源时调用。它需要一个指向信号量对象的指针作为参数。
  • sem_wait:用于减少信号量的值,通常在请求资源时调用。如果信号量的值为0,调用此函数的线程将会被阻塞,直到信号量的值大于0。
  • sem_trywait:与sem_wait类似,但它是非阻塞的。如果信号量的值为0,它不会阻塞线程,而是立即返回。
  • sem_getvalue:用于获取信号量的当前值。它需要一个指向信号量对象的指针和一个用于存储信号量值的指针作为参数。
  • sem_destroy:用于销毁一个无名信号量。它需要一个指向信号量对象的指针作为参数。

共享内存函数,包含在sys/mman.h中,链接时使用rt库:

  • mmap:把一个文件或一个POSIX共享内存区对象映射到调用进程的地址空间。使用该函数的目的: 1.使用普通文件以提供内存映射I/O 2.使用特殊文件以提供匿名内存映射。 3.使用shm_open以提供无亲缘关系进程间的Posix共享内存区。
  • munmap: 删除一个映射关系
  • msync:文件与内存同步函数
  • shm_open:创建或打开共享内存区
  • shm_unlink:删除一个共享内存区对象的名字,删除一个名字仅仅防止后续的open,msq_open或sem_open调用获取成功。
  • ftruncate:调整文件或共享内存区大小
  • fstat来获取有关该对象的信息

1.23 宏

  • PTHREAD_CREATE_JOINABLE:创建的线程可以被其他线程回收资源。
  • PTHREAD_CREATE_DETACHED:创建的线程是分离的,不需要其他线程回收资源。
  • PTHREAD_CANCEL_ENABLE:允许取消。
  • PTHREAD_CANCEL_DISABLE:禁止取消。
  • PTHREAD_PROCESS_PRIVATE:线程特定数据只在创建它的进程内可见。
  • PTHREAD_PROCESS_SHARED:线程特定数据可以在不同进程间共享。
  • PTHREAD_MUTEX_INITIALIZER:静态初始化互斥锁。
  • PTHREAD_COND_INITIALIZER:静态初始化条件变量。

二、创建线程

(1)定义线程函数:

线程函数是线程执行的入口点,它的定义形式如下:

void *thread_function(void *arg)
{
    // 线程执行的代码
    return NULL;
}

这个函数的参数和返回值类型都必须是void*

(2)创建线程:

使用pthread_create函数创建线程,函数原型如下:

int pthread_create(
	pthread_t *thread, 
	const pthread_attr_t *attr, 
	void *(*start_routine) (void *), 
	void *arg);

参数说明:

  • thread:指向线程标识符的指针,用于存储新创建的线程ID。
  • attr:指向线程属性的指针,用于设置线程的属性,如堆栈大小、调度策略等。如果设置为NULL,则使用默认属性。
  • start_routine:线程函数的起始地址。
  • arg:传递给线程函数的参数。

(3)等待线程结束:

使用pthread_join函数等待线程结束,函数原型如下:

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

参数说明:

  • thread:要等待的线程ID。
  • retval:用于存储线程函数的返回值。

例:

pthread_t tid; // 线程ID
pthread_create(&tid, NULL, myThreadFunction, NULL);

三、线程同步

前一篇文章讲了线程同步的方式。这里以互斥锁为例。

互斥锁(Mutex):互斥锁是一种用于保护共享资源的机制,确保同一时刻只有一个线程可以访问共享资源。pthread库提供了pthread_mutex_t类型的互斥锁。

  • 初始化互斥锁:使用pthread_mutex_init函数初始化互斥锁。
  • 锁定互斥锁:使用pthread_mutex_lock函数锁定互斥锁。
  • 解锁互斥锁:使用pthread_mutex_unlock函数解锁互斥锁。
  • 销毁互斥锁:使用pthread_mutex_destroy函数销毁互斥锁。

(1)定义和初始化互斥锁:

pthread_mutex_t mutex; // 定义互斥锁变量
  • 静态初始化:使用 PTHREAD_MUTEX_INITIALIZER 宏,这种方式初始化的互斥锁是静态的,不能销毁。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态初始化
  • 动态初始化:使用 pthread_mutex_init() 函数动态地初始化互斥锁,这种方式可以在需要的时候进行销毁。
pthread_mutex_t mutex; // 动态初始化
pthread_mutex_init(&mutex, NULL); // 动态初始化互斥锁

如果需要设置互斥锁的属性,可以创建一个 pthread_mutexattr_t 对象,设置属性,然后将其传递给 pthread_mutex_init() 函数的第二个参数。

(2) 在需要保护的临界区域加锁:

pthread_mutex_lock(&mutex);
// 访问共享资源的代码

(3)在使用完共享资源后解锁:

pthread_mutex_unlock(&mutex);

四、线程销毁

pthread_join(tid, NULL);

在 POSIX 线程(pthreads)编程中,销毁线程实际上是由操作系统自动完成的,当线程的运行终止时,它会被系统自动清理。

  1. 线程函数返回:线程函数执行完毕后,会返回一个值(通过 pthread_exit 或从线程函数返回),这会导致线程的终止。任何线程都能调用 pthread_exit 来结束自己的执行。

  2. 主线程等待子线程结束:使用 pthread_join 函数,主线程可以阻塞等待特定子线程结束。如果子线程已经结束,它的资源被释放,此时 pthread_join 返回。如果子线程还没有结束,主线程会被放入睡眠状态,直到子线程结束为止。

  3. 分离状态(Detach):如果不想使用 pthread_join 来等待子线程结束,你可以调用 pthread_detach 函数将线程置于分离状态。一旦线程处于分离状态,它将在终止时自动释放其资源,无需主线程显式等待。

  4. 取消线程:可以使用 pthread_cancel 函数来请求取消另一个线程的执行。被取消的线程可以选择清理并自行终止,或者立即终止,具体行为取决于该线程对取消状态的处理策略。

  5. 线程清理处理:当线程被取消或正常结束时,可以设置清理处理函数,这些函数会在线程退出前被调用,以执行必要的清理工作。可以使用 pthread_cleanup_push 注册这样的清理函数,并用 pthread_cleanup_pop 来配对它们。

  6. 线程资源的释放:线程结束时,它所占用的所有用户级资源应该已经被释放,例如动态分配的内存等。但是,线程使用的系统资源(如文件描述符、互斥锁等)通常由操作系统在线程结束后自动释放。

  7. 避免僵尸线程:在多线程程序中,确保所有线程都已经结束才让主线程退出是很重要的。否则,未被加入(joined)或分离(detached)的线程可能会变成所谓的“僵尸线程”,它们的进程状态不会释放,直到父进程结束。

  8. 错误检查:对于线程相关的函数调用(如 pthread_create, pthread_join, pthread_detach 等),应始终检查它们的返回值以确保没有发生错误。

五、示例

注意编译时指定链接库 -lpthread

5.1 完整示例

2个线程分别打印当前时间:线程创建后即运行,所以创建时间不同。

#include <stdio.h>
#include <unistd.h>
#include<pthread.h>
#include <sys/time.h>
#include<time.h>


void *print_time(void *arg){
    struct timeval current_time;
    if(gettimeofday(&current_time, NULL)==0){
       char *p = ctime(&current_time.tv_sec);
        printf("Current time is %s.\n",p);
    }
    else{
        return (void *)1;
    }
    return (void *)0;
}

int main(){
    pthread_t tid1,tid2;
    int ret1,ret2;

    ret1=pthread_create(&tid1, NULL,print_time,(void *)1);
    sleep(2);
    ret2=pthread_create(&tid2, NULL,print_time,(void *)2);

    if(ret1||ret2){
        printf("Creat threads ailed.\n");
        return 1;
    }

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    return 0;
}

在这里插入图片描述

5.2 信号量示例

现在修改一下,线程只有获得信号量的时候才能打印时间。

  • sem_init(&semaphore, 0, 1);:初始化信号量为 1,表示初始时可以访问临界区。
  • sem_wait(&semaphore);:在访问临界区之前等待信号量,如果为 0 则阻塞,直到变为大于 0 才继续执行。
  • sem_post(&semaphore);:在临界区访问结束后增加信号量,唤醒其他等待的线程。

信号量:

  • 信号量的作用
    -信号量是一种计数器,用于控制多个线程对共享资源的访问。
    • 当一个线程想要访问共享资源时,首先要等待信号量。
    • 如果信号量的值大于 0,表示资源空闲,线程可以继续执行,并将信号量减 1。
    • 如果信号量的值为 0,表示资源已被占用,线程会被阻塞,直到信号量的值变为大于 0。
  • 初始化:int sem_init(sem_t *restrict sem, int pshared, unsigned int value);
    • sem: 指向要初始化的信号量的指针。
    • pshared: 如果是0,则信号量是进程内共享的;如果是非0值,则用于进程间共享(但很少使用,通常不推荐,因为这涉及到进程间共享内存的复杂性)。
    • value: 信号量的初始值。
  • P、V 操作:
    • P操作 (sem_wait 或 sem_trywait): 减少信号量的值。如果信号量的值在操作之前是正的,它就简单地减一。如果信号量的值在操作之前是零,线程将被阻塞,直到信号量的值变成正数,然后它才减一。
    • V操作 (sem_post): 增加信号量的值。这通常用来表示一个线程已经释放了它所持有的资源。
  • 信号量分类:
    • 二值信号量:通常用于实现互斥(Mutual Exclusion),确保在任何时刻只有一个线程或进程可以访问关键部分代码或资源。它只有两个值,通常是0(表示资源不可用)和1(表示资源可用)。当一个线程尝试获取值为0的信号量时,它将被阻塞直到信号量的值变为1。
    • 计数信号量:是更一般化的同步机制,允许一定数量的线程同时访问某个资源。它的值可以是任何非负整数,代表可用资源的数量。当一个线程想要访问资源时,它会执行P操作,使信号量减一;当线程释放资源时,它会执行V操作,使信号量加一。如果信号量的值小于或等于0,则到达的线程将被阻塞。
      在这里插入图片描述
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <time.h>

sem_t semaphore;

void* printTime(void* arg) {
    sem_wait(&semaphore);
    time_t now;
    struct tm* timeinfo;
    time(&now);
    timeinfo = localtime(&now);
    printf("Thread ID: %ld, Current Time: %s", pthread_self(), asctime(timeinfo));
    sleep(2);  
    sem_post(&semaphore);
    return NULL;
}

int main() {
    pthread_t tid1, tid2;
    
    sem_init(&semaphore, 0, 1); // 初始化信号量为 1
    
    pthread_create(&tid1, NULL, printTime, NULL);
    pthread_create(&tid2, NULL, printTime, NULL);
    
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    
    sem_destroy(&semaphore);
    
    return 0;
}

在这里插入图片描述

把信号量初始值改为2,则2个线程可以同时打印时间:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ELK日志管理实现的3种常见方法

ELK日志管理实现的3种常见方法 1. 日志收集方法 1.1 使用DaemonSet方式日志收集 通过将node节点的/var/log/pods目录挂载给以DaemonSet方式部署的logstash来读取容器日志,并将日志吐给kafka并分布写入Zookeeper数据库.再使用logstash将Zookeeper中的数据写入ES,并通过kibana…

如何利用百度SEO优化技巧将排到首页

拥有一个成功的网站对于企业和个人来说是至关重要的&#xff0c;在当今数字化的时代。在互联网上获得高流量和优质的访问者可能并不是一件容易的事情&#xff0c;然而。一个成功的SEO战略可以帮助你实现这一目标。需要一些特定的技巧和策略、但要在百度搜索引擎中获得较高排名。…

某狗网翻译接口逆向之webpack扣取

​​​​​逆向网址 aHR0cHM6Ly9mYW55aS5zb2dvdS5jb20 逆向链接 aHR0cHM6Ly9mYW55aS5zb2dvdS5jb20vdGV4dA 逆向接口 aHR0cHM6Ly9mYW55aS5zb2dvdS5jb20vYXBpL3RyYW5zcGMvdGV4dC9yZXN1bHQ 逆向过程 请求方式&#xff1a;POST 参数构成&#xff1a; 【s】 1b921dbefaa8d939afca…

了解常用测试模型 -- 瀑布模型、螺旋模型、增量与迭代、敏捷开发

目录 瀑布模型 开发流程 开发特征 优缺点 适用场景 螺旋模型 开发流程 开发特征 优缺点 适用场景 增量与迭代开发 什么是增量开发&#xff1f;什么是迭代开发&#xff1f; 敏捷开发 什么是敏捷开发四原则&#xff08;敏捷宣言&#xff09;&#xff1f; 什么是 s…

Pytorch从零开始实战21

Pytorch从零开始实战——Pix2Pix理论与实战 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——Pix2Pix理论与实战内容介绍数据集加载模型实现开始训练总结 内容介绍 Pix2Pix是一种用于用于图像翻译的通用框架&#xff0c;即图像到图像的转换。…

手撕HashMap底层源码 (JDK1.7版本的HashMap)

day27 集合框架 标绿已经学习底层&#xff0c;深入底层主要是研究实现类底层 手撕HashMap底层源码 JDK1.7版本的HashMap 切换版本 原因&#xff1a;jdk1.7和jdk1.8的HashMap不同&#xff08;头插法/尾插法&#xff09; 首先如果没有jdkjre1.7&#xff0c;就安装jdkjre1.7&am…

mysql数据库备份学习笔记

数据库备份 方法1 物理备份&#xff1a;xtrabackup 方法2 逻辑备份 mysqldump 参考备份与恢复的方法&#xff1a; 【MySql】Mysql之备份与恢复_mysql数据库备份与还原-CSDN博客 可以借鉴的物理备份&#xff1a; 思路是 先做一次全量备份&#xff0c;然后每天做一次增量备份…

从大模型到Agentscope——进阶: 容错机制与构建多模态应用

文章目录 容错机制与构建多模态应用高鲁棒的容错机制鲁棒性的挑战可访问性错误规则可解错误&模型可解错误不可解错误 设计理念&#xff1a;多模态 容错机制与构建多模态应用 高鲁棒的容错机制 鲁棒性的挑战 可访问性错误 默认重新调用三次并把报错给打印出来 规则可解错误…

嵌入式单片机学习思路感想分享

今天看到了一个提问,原话如下: 曾经干了8年单片机工程师,对工程师从入门,到入行,再到普通,再到高级,整个路径还算清晰,比如什么阶段,会碰到什么瓶颈,怎么突破,我都经历过。 这个同学,有个典型的问题,就是学得太多且杂了,估计稍微复杂点的项目,做不出来。 现在…

长期护理保险可改善老年人心理健康 | CHARLS CLHLS CFPS 公共数据库周报(3.6)...

欢迎报名2024年“真实世界临床研究”课程&#xff01; 本周郑老师开讲&#xff1a;“真实世界临床研究”培训班&#xff0c;3月16-17日两天&#xff0c;欢迎报名&#xff01; CHARLS公共数据库‍ CHARLS数据库简介中国健康与养老追踪调查(China Health and Retirement Longitud…

.NET高级面试指南专题十八【 外观模式模式介绍,提供了简化的接口,隐藏系统的复杂性】

介绍&#xff1a; 外观模式是一种结构设计模式&#xff0c;它提供了一个统一的接口&#xff0c;用于访问子系统中的一组接口。外观模式定义了一个高层接口&#xff0c;使得子系统更容易使用。 原理&#xff1a; 外观类&#xff08;Facade Class&#xff09;&#xff1a;提供了一…

电脑芯片维修费用、价格方面的处理方法有哪些?

电脑使用时间长了&#xff0c;难免会出现这样或那样的问题。 笔者整理了一些关于电脑芯片维修费用和价格的参考资料&#xff0c;以及简单的处理方法&#xff0c;供大家了解和学习&#xff1a; 1、笔记本电脑芯片维修&#xff1a; 首先检查电脑系统是否完好。 如果是系统软故障…

【Qt】常用控件或属性(1)

需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 一、QWidget属性一览 二、控件button、属性enabled(可用状态) 三、属性geometry(修改位置和尺寸) 1、QRect类型的结…

数字化转型导师坚鹏:金融机构数字化运营

金融机构数字化运营 课程背景&#xff1a; 很多金融机构存在以下问题&#xff1a; 不清楚数字化运营对金融机构发展有什么影响&#xff1f; 不知道如何提升金融机构数字化运营能力&#xff1f; 不知道金融机构如何开展数字化运营工作&#xff1f; 课程特色&#xff1a;…

Vue2 引入自己下载的SVG图像的方式

Vue2 引入下载的SVG图像的方式 Step 1&#xff1a;安装依赖 npm i svg-sprite-loader --saveStep 2&#xff1a;创建文件路径 // index.js import Vue from vue import SvgIcon from /components/SvgIcon// svg component// register globally Vue.component(svg-icon, Svg…

PHP序列化基础知识储备

一、序列化与反序列化 1、概念 PHP中的序列化是指将复杂的数据类型转换为可存储或可传输的字符串&#xff0c;而反序列化则是将这些字符串重新转换回原来的数据类型。 序列化通常使用 serialize() 函数完成&#xff0c;它可以将数组、对象、字符串等复杂数据类型压缩到一个字…

在Linux/Ubuntu/Debian中使用7z压缩和解压文件

要在 Ubuntu 上使用 7-Zip 创建 7z 存档文件&#xff0c;你可以使用“7z”命令行工具。 操作方法如下&#xff1a; 安装 p7zip&#xff1a; 如果你尚未在 Ubuntu 系统上安装 p7zip&#xff08;7-Zip 的命令行版本&#xff09;&#xff0c;你可以使用以下命令安装它&#xff1a;…

数据结构:堆

堆的概念 1.堆是一个完全二叉树 2.小堆(任何一个父亲<孩子),大堆(任何一个父亲>孩子) 堆的结构 物理结构:数组 逻辑结构:二叉树 #pragma once #include<assert.h> #include<iostream> typedef int HPDataType; typedef struct Heap {HPDataType* _a;int…

手写Mybatis自动填充插件

目录 一、Mybatis插件简介&#x1f959;二、工程创建及前期准备工作&#x1f96b;实现代码配置文件 三、插件核心代码实现&#x1f357;四、测试&#x1f953; 一、Mybatis插件简介&#x1f959; Mybatis插件运行原理及自定义插件_简述mybatis的插件运行原理,以及如何编写一个…

Java8 Stream流详解

文章目录 概念和特点基本流程常见操作过滤&#xff08;filter&#xff09;映射&#xff08;map&#xff09;收集&#xff08;collect&#xff09;归约&#xff08;reduce&#xff09;扁平映射&#xff08;flatMap&#xff09; java案例详解注意事项 概念和特点 Java中的Stream是…