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

学习记录——day25 多线程编程 临界资源 临界区 竞态 线程的同步互斥机制(用于解决竟态)

目录

​编辑

一、多进程与多线程对比

二、 临界资源   临界区   竞态

 例1:临界资源 实现 输入输出

 例2:对临界资源 进行 减减 

例子3:临界资源抢占使用

三、线程的同步互斥机制(用于解决竟态)

3.1基本概念

3.2线程互斥

1、在C语言中,线程的互斥通过互斥锁来完成

2、互斥锁:本质上是一种临界资源,但互斥锁在同一时刻只能被一个线程使用,当一个线程试图去锁定另一个线程锁定的互斥所时,该线程会阻塞等待,直到使用互斥锁的线程解锁了该互斥锁

3、互斥锁的相关API

 4、对例3进行  互斥

3.3死锁

 3.4线程同步        

3.5线程同步 无名信号量

3.6 线程同步 条件变量


一、多进程与多线程对比

二、 临界资源   临界区   竞态

 例1:临界资源 实现 输入输出

#include <myhead.h>
#define N 2char buf[128] = "";//临界资源// 定义线程体1  使用临界资源的代码 称为临界区
void *task1(void *arg)
{while (1) // 输入信息{printf("输入:");fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = 0;}
}// 定义线程体2  使用临界资源的代码 称为临界区
void *task2(void *arg)
{while (1) // 循环输出信息{usleep(500000); // 500000us  0.5s  执行一次printf("buf = %s\n", buf);bzero(buf, sizeof(buf));}
}
int main(int argc, char const *argv[])
{// 创建两个任务pthread_t tid1, tid2;if (pthread_create(&tid1, NULL, task1, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid2, NULL, task2, NULL) != 0){printf("task2 error");return -1;}// 主程序printf("tid1 = %#lx, tid2 = %#lx\n", tid1, tid2);// 阻塞回收线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}

 例2:对临界资源 进行 减减 

#include <myhead.h>
#define N 2int num  = 520;//临界资源// 定义线程体1  使用临界资源的代码 称为临界区
void *task1(void *arg)
{while (1) // 输入信息{if (num > 0){num--;printf("task1 执行一次,剩余%d次\n",num);usleep(1000);}else{printf("task1 执行结束\n");break;}}pthread_exit(EXIT_SUCCESS);
}// 定义线程体2  使用临界资源的代码 称为临界区
void *task2(void *arg)
{while (1) // 输入信息{if (num > 0){num--;printf("task2 执行一次,剩余%d次\n",num);usleep(1000);}else{printf("task2 执行结束\n");break;}}pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{// 创建两个任务pthread_t tid1, tid2;if (pthread_create(&tid1, NULL, task1, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid2, NULL, task2, NULL) != 0){printf("task2 error");return -1;}// 主程序printf("tid1 = %#lx, tid2 = %#lx\n", tid1, tid2);// 阻塞回收线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}

例子3:临界资源抢占使用

#include <myhead.h>
#define N 2int num  = 520;//临界资源// 定义线程体1  使用临界资源的函数 称为临界区
void *task1(void *arg)
{while (1) // 输入信息{if (num > 0){num--;printf("task1 执行一次,剩余%d次\n",num);usleep(1000);}else{printf("task1 执行结束\n");break;}}pthread_exit(EXIT_SUCCESS);
}// 定义线程体2  使用临界资源的函数 称为临界区
void *task2(void *arg)
{while (1) // 输入信息{if (num > 0){num--;printf("task2 执行一次,剩余%d次\n",num);usleep(1000);}else{printf("task2 执行结束\n");break;}}pthread_exit(EXIT_SUCCESS);
}// 定义线程体1  使用临界资源的函数 称为临界区
void *task3(void *arg)
{while (1) // 输入信息{if (num > 0){num--;printf("task3 执行一次,剩余%d次\n",num);usleep(1000);}else{printf("task3 执行结束\n");break;}}pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{// 创建三个任务pthread_t tid1, tid2,tid3;if (pthread_create(&tid1, NULL, task1, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid2, NULL, task2, NULL) != 0){printf("task2 error");return -1;}if (pthread_create(&tid3, NULL, task3, NULL) != 0){printf("task2 error");return -1;}// 主程序printf("tid1 = %#lx, tid2 = %#lx\n", tid1, tid2);// 阻塞回收线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);return 0;
}

        由于多个线程共同使用进程的资源,导致,线程在操作上容易出现不安全的状态

        对于例3 减减 含有多步操作 相对耗时 在多个线程对临界资源抢占使用时,会出现某一个线程的 减减 未结束另一个线程的 开始执行,就会导致数据出错。

        该现象称为 竞态

三、线程的同步互斥机制(用于解决竟态)

        如上例3所示,当其中一个线程正在访问全局变量时,可能会出现时间片用完的情况,另一个线程将数据进行更改后,再执行 该线程时,导致数据的不一致。这种现象称竞态,为了解决竞态,引入了同步互斥机制

3.1基本概念

1)竟态:同一个进程的多个线程在访问临界资源时,会出现抢占的现象,导致线程中的数据错误的现象称为竞态

2)临界资源:多个线程共同访问的数据称为临界资源,可以是全局变量、堆区数据、外部文件

3)临界区:访问临界资源的代码段称为临界区

4)粒度:临界区的大小

5)同步:表示多个任务有先后顺序的执行

6)互斥:表示在访问临界区时,同一时刻只能有一个任务,当有任务在访问临界区时,其他任务只能等待

3.2线程互斥

1、在C语言中,线程的互斥通过互斥锁来完成

2、互斥锁:本质上是一种临界资源,但互斥锁在同一时刻只能被一个线程使用,当一个线程试图去锁定另一个线程锁定的互斥所时,该线程会阻塞等待,直到使用互斥锁的线程解锁了该互斥锁

3、互斥锁的相关API

       #include <pthread.h>
       1、创建互斥锁:只需要定义一个pthread_mutex_t类型的变量,就创建了一个互斥锁

       pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;           //静态初始化一个互斥锁

       2、初始化互斥锁

       int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
       功能:初始化一个互斥锁
       参数1:互斥锁变量的地址
       参数2:互斥锁的属性,一般填NULL,使用系统默认提供的属性
       返回值:总是成功,不会失败(成功返回0)
        
       3、获取锁资源

       int pthread_mutex_lock(pthread_mutex_t *mutex);
       功能:某个线程调用该函数表示获取锁资源(阻塞等待)
       参数:互斥锁的地址
       返回值:成功返回0,失败返回一个错误码
       
       4、释放锁资源

        int pthread_mutex_unlock(pthread_mutex_t *mutex);
       功能:将线程中的锁资源释放
       参数:互斥锁的地址
       返回值:成功返回0,失败返回一个错误码
       
        5、销毁锁资源
       int pthread_mutex_destroy(pthread_mutex_t *mutex);
       功能:销毁程序中的锁资源
       参数:互斥锁地址        

 4、对例3进行  互斥

#include <myhead.h>
#define N 2int num = 520; // 临界资源// 创建一个互斥锁
pthread_mutex_t mutex;// 定义线程体1  使用临界资源的代码 称为临界区
void *task1(void *arg)
{while (1) // 输入信息{// 上锁  获取锁资源pthread_mutex_lock(&mutex);if (num > 0){num--;printf("task1 执行一次,剩余%d次\n", num);usleep(1000);}else{printf("task1 执行结束\n");// 解锁 释放锁资源pthread_mutex_unlock(&mutex); // 结束前解锁 避免死锁break;}// 解锁 释放锁资源pthread_mutex_unlock(&mutex);}pthread_exit(EXIT_SUCCESS);
}// 定义线程体2  使用临界资源的代码 称为临界区
void *task2(void *arg)
{while (1) // 输入信息{// 上锁  获取锁资源pthread_mutex_lock(&mutex);if (num > 0){num--;printf("task2 执行一次,剩余%d次\n", num);usleep(1000);}else{printf("task2 执行结束\n");// 解锁 释放锁资源pthread_mutex_unlock(&mutex); // 避免死锁break;}// 解锁 释放锁资源pthread_mutex_unlock(&mutex);}pthread_exit(EXIT_SUCCESS);
}// 定义线程体3  使用临界资源的函数 称为临界区
void *task3(void *arg)
{while (1) // 输入信息{// 上锁  获取锁资源pthread_mutex_lock(&mutex);if (num > 0){num--;printf("task3 执行一次,剩余%d次\n", num);usleep(1000);}else{printf("task3 执行结束\n");// 解锁 释放锁资源pthread_mutex_unlock(&mutex); // 避免死锁break;}// 解锁 释放锁资源pthread_mutex_unlock(&mutex);}pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{//初始化锁pthread_mutex_init(&mutex, NULL);// 创建三个任务pthread_t tid1, tid2, tid3;if (pthread_create(&tid1, NULL, task1, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid2, NULL, task2, NULL) != 0){printf("task2 error");return -1;}if (pthread_create(&tid3, NULL, task3, NULL) != 0){printf("task2 error");return -1;}// 主程序printf("tid1 = %#lx, tid2 = %#lx\n", tid1, tid2);// 阻塞回收线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);// 销毁锁资源pthread_mutex_destroy(&mutex);return 0;
}

3.3死锁

1、概念:在多线程编程中,死锁是一种情况,其中两个或多个线程被永久阻塞,因为每个线程都在等待其他线程释放它们需要的资源。在C语言中,这通常涉及互斥锁(mutexes),当多个互斥锁被不同的线程以不同的顺序获取时,很容易发生死锁。

2、产生条件:

1) 互斥条件:资源不能被多个线程共享,只能被一个线程在任一时刻所使用。
2) 持有和等待条件:一个线程至少持有一个资源,并且正在等待获取一个当前被其他线程持有的资源。
3.)不可抢占条件:资源不能被强制从一个线程抢占到另一个线程,线程必须自愿释放它的资源。
4.)循环等待条件:存在一个线程(或多个线程)的集合{P1, P2, ..., Pn},其中P1正在等待P2持有的资源,P2正在等待P3持有的资源,依此类推,直至Pn正在等待P1持有的资源。

3、例4:死锁示例

#include <pthread.h>
#include <stdio.h>pthread_mutex_t lock1, lock2;void* thread1(void* arg) {pthread_mutex_lock(&lock1);sleep(1); // 确保线程2能锁住lock2pthread_mutex_lock(&lock2);printf("Thread 1\n");pthread_mutex_unlock(&lock2);pthread_mutex_unlock(&lock1);return NULL;
}void* thread2(void* arg) {pthread_mutex_lock(&lock2);sleep(1); // 确保线程1能锁住lock1pthread_mutex_lock(&lock1);printf("Thread 2\n");pthread_mutex_unlock(&lock1);pthread_mutex_unlock(&lock2);return NULL;
}int main() {pthread_t t1, t2;pthread_mutex_init(&lock1, NULL);pthread_mutex_init(&lock2, NULL);pthread_create(&t1, NULL, thread1, NULL);pthread_create(&t2, NULL, thread2, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_mutex_destroy(&lock1);pthread_mutex_destroy(&lock2);return 0;
}

4、避免死锁的策略:

1.)避免持有和等待:尽可能让线程在开始执行前一次性获取所有必需的资源。
2.)资源排序:规定一个全局顺序来获取资源,并且强制所有线程按这个顺序获取资源。
3.)使用超时:在尝试获取资源时使用超时机制,这样线程在等待过长时间后可以放弃,回退,并重新尝试。
4.)检测死锁并恢复:运行时检测死锁的存在,一旦检测到死锁,采取措施(如终止线程或回滚操作)来解决。

 3.4线程同步        

1、多个线程任务有顺序的执行,由于多个任务有顺序的执行了,那么在同一时刻,对临界资源的访问就只一个线程

2、线程同步文件的经典问题是:生产者消费者问题

        对于该问题而言,需要先执行生产者任务,然后执行消费者任务

3、对于线程同步问题,有两种机制来完成:无名信号量、条件变量

3.5线程同步 无名信号量

1、无名信号量本质上也是一个特殊的临界资源

2、无名信号量中维护了一个value值,该值表示能够申请的资源个数,当该值为0时,申请资源的线程将处于阻塞,直到其他线程将该无名信号量中的value值增加到大于0时即可

3、无名信号量API

1、创建无名信号量:只需要定义一个 sem_t 类型的变量,就创建了一个无名信号量
    sem_t  sem;
2、初始化无名信号量
           #include <semaphore.h>

       int sem_init(sem_t *sem, int pshared, unsigned int value);
       功能:完成对无名信号量的初始化工作
       参数1:要被初始化的无名信号量地址
       参数2:无名信号量适用情况
           0:表示多线程之间
           非0:表示多进程间同步
        参数3:无名信号量的资源初始值
        返回值:成功返回0,失败返回-1并置位错误码

3、申请资源:P操作,将资源减1操作
           #include <semaphore.h>

       int sem_wait(sem_t *sem);
       功能:申请无名信号量中的资源,使得该信号量中的value-1
       参数:无名信号量地址
       返回值:成功返回0,失败返回-1并置位错误码

4、释放资源:V操作,将资源加1操作
               #include <semaphore.h>

       int sem_post(sem_t *sem);
       功能:将无名信号量中的资源加1操作
       参数:无名信号量地址
       返回值:成功返回0,失败返回-1并置位错误码

5、销毁无名信号量
       #include <semaphore.h>

       int sem_destroy(sem_t *sem);
       功能:销毁一个无名信号量
       参数:无名信号量地址
       返回值:成功返回0,失败返回-1并置位错误码

 4、同步  无名信号量 示例

未 使用 无名信号量

#include <myhead.h>//创建生产者线程
void * task1(void *arg)
{int num = 5;while (num--){printf("%#lx:执行一次生产\n",pthread_self());sleep(1);}pthread_exit(EXIT_SUCCESS);
}//创建消费者线程
void * task2(void *arg)
{int num = 5;while (num--){printf("%#lx:执行一次消费\n",pthread_self());}pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{pthread_t tid1, tid2;if (pthread_create(&tid1, NULL, task1, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid2, NULL, task2, NULL) != 0){printf("task1 error");return -1;}pthread_join(tid2, NULL);pthread_join(tid1, NULL);return 0;
}

 

 使用 无名信号量

#include <myhead.h>//1、创建无名学号量
sem_t sem;//创建生产者线程
void * task1(void *arg)
{int num = 5;while (num--){printf("%#lx:执行一次生产\n",pthread_self());//4、释放资源 无名信号量资源加1 允许消费者a线程执行sem_post(&sem);}pthread_exit(EXIT_SUCCESS);
}//创建消费者线程
void * task2(void *arg)
{int num = 5;while (num--){//3、申请资源sem_wait(&sem); //阻塞等待  生产者线程释放 无名信号量资源//当无名信号量资源 为1时 执行 减1//因为 无名信号量 初始资源为0  即便先执行消费者线程也会被阻塞printf("%#lx:执行一次消费\n",pthread_self());}pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{//2、初始化无名信号量sem_init(&sem,0,0);//第一个0   表示同步用于多线程间//第二e个0  表示无名信号量初始资源为0pthread_t tid1, tid2;if (pthread_create(&tid1, NULL, task1, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid2, NULL, task2, NULL) != 0){printf("task1 error");return -1;}pthread_join(tid2, NULL);pthread_join(tid1, NULL);//5、销毁无名信号量sem_destroy(&sem);return 0;
}

3.6 线程同步 条件变量

1、条件变量的本质也是一个特殊的临界资源

2、条件变量中维护了一个队列,想要执行的消费者线程,需要先进入等待队列中,等生产者线程进行唤醒后,依次执行。这样就可以做到一个生产者和多个消费者之间的同步,但是消费者和消费者之间在进入等待队列这件事上是互斥的。

3、条件变量的API

1)创建条件变量
    只需要定义一个pthread_cond_t类型的变量,就创建了一个条件变量
    pthread_cond_t  cond;

2)初始化条件变量
       int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
       功能:初始化一个条件变量
       参数1:条件变量的地址
       参数2:条件变量的属性,一般填NULL
       返回值:     成功返回0,失败返回非0错误码
       

3)将消费者线程放入到等待队列中       

       int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
       功能:消费者线程进入等待队列中
       参数1:条件变量的地址
       参数2:互斥锁的地址:因为多个消费者线程在进入等待队列上是竞态的
       返回值:     成功返回0,失败返回非0错误码

4)唤醒消费者线程                                

       int pthread_cond_signal(pthread_cond_t *cond);
       功能:唤醒等待队列中的第一个消费者线程
       参数:条件变量的地址
       返回值: 成功返回0,失败返回非0错误码       

       

       int pthread_cond_broadcast(pthread_cond_t *cond);
       功能:唤醒所有等待队列中的消费者线程
       参数:条件变量的地址
       返回值: 成功返回0,失败返回非0错误码
       

    
5)销毁条件变量           
        int pthread_cond_destroy(pthread_cond_t *cond);
        功能:销毁一个条件变量
        参数:条件变量的地址
        返回值: 成功返回0,失败返回非0错误码

 4、条件变量 示例

#include <myhead.h>//1、定义一个条件变量
pthread_cond_t cond;//11、创建互斥锁
pthread_mutex_t mutex;// 创建生产者线程
void *task1(void *arg)
{// int num = 5;// while (num--)// {//     sleep(1);//     printf("%#lx:执行一次生产\n", pthread_self());//     //4、唤醒消费者线程(单个)//     pthread_cond_signal(&cond);// }sleep(3);printf("%#lx:执行5次生产\n", pthread_self());pthread_cond_broadcast(&cond);//4、唤醒消费者线程(全部)pthread_exit(EXIT_SUCCESS);
}// 创建消费者线程
void *task2(void *arg)
{//33、获取互斥锁pthread_mutex_lock(&mutex);//3、请求进入等待队列pthread_cond_wait(&cond,&mutex);printf("%#lx:执行一次消费\n", pthread_self());//44、释放互斥锁pthread_mutex_unlock(&mutex);pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{//2、初始化统计变量pthread_cond_init(&cond,NULL);//22、初始化互斥锁pthread_mutex_init(&mutex,NULL);pthread_t tid1, tid2, tid3, tid4, tid5, tid6;if (pthread_create(&tid1, NULL, task1, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid2, NULL, task2, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid3, NULL, task2, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid4, NULL, task2, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid5, NULL, task2, NULL) != 0){printf("task1 error");return -1;}if (pthread_create(&tid6, NULL, task2, NULL) != 0){printf("task1 error");return -1;}//输出每个线程的线程号printf("tid2=%#lx tid3=%#lx tid4=%#lx tid5=%#lx tid6=%#lx \n",tid2,tid3,tid4,tid5,tid6);pthread_join(tid2, NULL);pthread_join(tid1, NULL);pthread_join(tid3, NULL);pthread_join(tid4, NULL);pthread_join(tid5, NULL);pthread_join(tid6, NULL);//5、销毁条件变量pthread_cond_destroy(&cond);//55、销毁锁资源pthread_mutex_destroy(&mutex);return 0;
}

 

相关文章:

学习记录——day25 多线程编程 临界资源 临界区 竞态 线程的同步互斥机制(用于解决竟态)

目录 ​编辑 一、多进程与多线程对比 二、 临界资源 临界区 竞态 例1&#xff1a;临界资源 实现 输入输出 例2&#xff1a;对临界资源 进行 减减 例子3&#xff1a;临界资源抢占使用 三、线程的同步互斥机制&#xff08;用于解决竟态&#xff09; 3.1基本概念 3.2线…...

[RK3566]linux下使用upgrade_tool报错

linux下使用upgrade_tool报错Creating Comm Object failed! Rockusb>uf /home/zhuhongxi/RK3566_AOSP_SDK/rockdev/Image-rk3566_tspi/update.img Loading firmware... Support Type:RK3568 FW Ver:b.0.00 FW Time:2024-08-03 12:00:09 Loader ver:1.01 Loader Time:…...

系统架构师(每日一练13)

每日一练 答案与解析 1.应用系统构建中可以采用多种不同的技术&#xff0c;()可以将软件某种形式的描述转换为更高级的抽象表现形式&#xff0c;而利用这些获取的信息&#xff0c;()能够对现有系统进行修改或重构&#xff0c;从而产生系统的一个新版本。答案与解析 问题1 A.逆…...

Error: No module factory available for dependency type: CssDependency

本篇主要用来记录VUE打包的问题点&#xff0c;今天使用npm run build:prod 打包VUE出现如下问题&#xff1a; Error: No module factory available for dependency type: CssDependency 因为测试和预发布都挺正常的&#xff0c;正式环境竟然出问题&#xff0c;废话不多说&…...

【langchain学习】使用Langchain生成多视角查询

使用Langchain生成多视角查询 导入所需库&#xff1a; from langchain.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough from config import llm设置提示模板&#x…...

ASPCMS 漏洞详细教程

一.后台修改配置文件拿shell 登录后台 如下操作 保存并抓包 将slideTextStatus的值修改为1%25><%25Eval(Request(chr(65)))25><%25 放包&#xff08;连接密码是a&#xff09; 然后用工具连接 成功连接...

二维码生成原理及解码原理

☝☝☝二维码配图 二维码 二维码&#xff08;Quick Response Code&#xff0c;简称QR码&#xff09;是一种广泛使用的二维条形码技术&#xff0c;由日本公司Denso Wave在1994年开发。二维码能有效地存储和传递信息&#xff0c;广泛应用于商品追溯、支付、广告等多个领域。二维…...

云计算实训20——mysql数据库安装及应用(增、删、改、查)

一、mysql安装基本步骤 1.下载安装包 wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar 2.解压 tar -xf mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar 3.卸载mariadb yum -y remove mariadb 查看解压后的包 [rootmysq…...

24年电赛——自动行驶小车(H题)基于 CCS Theia -陀螺仪 JY60 代码移植到 MSPM0G3507(附代码)

前言 只要搞懂 M0 的代码结构和 CCS 的图形化配置方法&#xff0c;代码移植就会变的很简单。因为本次电赛的需要&#xff0c;正好陀螺仪部分代码的移植是我完成的。&#xff08;末尾附全部代码&#xff09; 一、JY60 陀螺仪 JY60特点 1.模块集成高精度的陀螺仪、加速度计&…...

数组的增删查查改

1、增 1.Cpp #include <iostream> using namespace std; #include "add.h"int main() {//初始化数组int arr[5];//前四个元素为1&#xff0c;2&#xff0c;3&#xff0c;4for (int i 0; i < 4; i){arr[i] i1;}//数组第5个赋值为100arr[4] 100;for (int…...

设计模式——动态代理

设计模式——动态代理 动态代理的基本概念动态代理的实现步骤总结 在Java中&#xff0c;动态代理是一种强大的机制&#xff0c;它允许在运行时创建一个代理对象&#xff0c;这个代理对象可以代表另一个实际对象&#xff0c;它允许你在不直接操作原始对象的情况下&#xff0c;通…...

vue(element-ui组件) 的this.$notify的具体使用

getNotify() {this.noClose();let message "";message this.itemData.map((ele) > {const text "任务" ele.title "新增" ele.num "条言论";return this.$createElement("el-tooltip",{props: {content: text,pla…...

c++ - 模拟实现set、map

文章目录 前言一、set模拟实现二、map模拟实现 前言 在C标准库中&#xff0c;std::set 和 std::map都是非常常用的容器&#xff0c;它们提供了基于键值对的存储和快速查找能力。然而&#xff0c;关于它们的底层实现&#xff0c;C标准并没有强制规定具体的数据结构&#xff0c;只…...

计算机网络-PIM协议基础概念

一、PIM基础概念 组播网络回顾&#xff1a; 组播网络从网络结构上大体可以分为三个部分&#xff1a; 源端网络&#xff1a;将组播源产生的组播数据发送至组播网络。 组播转发网络&#xff1a;形成无环的组播转发路径&#xff0c;该转发路径也被称为组播分发树&#xff08;Multi…...

优化PyCharm:让IDE响应速度飞起来

优化PyCharm&#xff1a;让IDE响应速度飞起来 PyCharm&#xff0c;作为一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;在提供丰富功能的同时&#xff0c;有时也会出现响应慢的问题。这不仅影响开发效率&#xff0c;还可能打击开发者的积极性。本文将详细…...

对象转化为String,String转化为对象

title: 对象转化为string&#xff0c;string转化为对象 date: 2024-08-02 11:50:40 tags: javascript const obj { uname:haha, age:18,gender:女} //将对象转换成string JSON.stringify(obj) //取成一个对象&#xff0c;将字符串传化为对象 JSON.parse(obj)常用领域在localst…...

SolverLearner:提升大模型在高度归纳推理的复杂任务性能,使其能够在较少的人为干预下自主学习和适应

SolverLearner&#xff1a;提升大模型在高度归纳推理的复杂任务性能&#xff0c;使其能够在较少的人为干预下自主学习和适应 提出背景归纳推理&#xff08;Inductive Reasoning&#xff09;演绎推理&#xff08;Deductive Reasoning&#xff09;反事实推理&#xff08;Counterf…...

PHP智能问诊导诊平台-计算机毕业设计源码75056

摘 要 智能问诊导诊平台作为一种智能化医疗服务工具&#xff0c;利用PHP语言开发&#xff0c;旨在为用户提供便捷的在线问诊和导诊服务。该平台集成了智能算法和医疗数据&#xff0c;实现了智能化的病情诊断和治疗建议&#xff0c;帮助用户更快速地获取医疗信息和建议。用户可…...

数据结构初阶(c语言)-排序算法

数据结构初阶我们需要了解掌握的几种排序算法(除了直接选择排序&#xff0c;这个原因我们后面介绍的时候会解释)如下&#xff1a; 其中的堆排序与冒泡排序我们在之前的文章中已经详细介绍过并对堆排序进行了一定的复杂度分析&#xff0c;所以这里我们不再过多介绍。 一&#x…...

网络云相册实现--nodejs后端+vue3前端

目录 主页面 功能简介 系统简介 api 数据库表结构 代码目录 运行命令 主要代码 server apis.js encry.js mysql.js upload.js client3 index.js 完整代码 主页面 功能简介 多用户系统&#xff0c;用户可以在系统中注册、登录及管理自己的账号、相册及照片。 每…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...