学习记录——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:临界资源 实现 输入输出 例2:对临界资源 进行 减减 例子3:临界资源抢占使用 三、线程的同步互斥机制(用于解决竟态) 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.应用系统构建中可以采用多种不同的技术,()可以将软件某种形式的描述转换为更高级的抽象表现形式,而利用这些获取的信息,()能够对现有系统进行修改或重构,从而产生系统的一个新版本。答案与解析 问题1 A.逆…...
Error: No module factory available for dependency type: CssDependency
本篇主要用来记录VUE打包的问题点,今天使用npm run build:prod 打包VUE出现如下问题: Error: No module factory available for dependency type: CssDependency 因为测试和预发布都挺正常的,正式环境竟然出问题,废话不多说&…...
【langchain学习】使用Langchain生成多视角查询
使用Langchain生成多视角查询 导入所需库: 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 放包(连接密码是a) 然后用工具连接 成功连接...
二维码生成原理及解码原理
☝☝☝二维码配图 二维码 二维码(Quick Response Code,简称QR码)是一种广泛使用的二维条形码技术,由日本公司Denso Wave在1994年开发。二维码能有效地存储和传递信息,广泛应用于商品追溯、支付、广告等多个领域。二维…...
云计算实训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 的图形化配置方法,代码移植就会变的很简单。因为本次电赛的需要,正好陀螺仪部分代码的移植是我完成的。(末尾附全部代码) 一、JY60 陀螺仪 JY60特点 1.模块集成高精度的陀螺仪、加速度计&…...
数组的增删查查改
1、增 1.Cpp #include <iostream> using namespace std; #include "add.h"int main() {//初始化数组int arr[5];//前四个元素为1,2,3,4for (int i 0; i < 4; i){arr[i] i1;}//数组第5个赋值为100arr[4] 100;for (int…...
设计模式——动态代理
设计模式——动态代理 动态代理的基本概念动态代理的实现步骤总结 在Java中,动态代理是一种强大的机制,它允许在运行时创建一个代理对象,这个代理对象可以代表另一个实际对象,它允许你在不直接操作原始对象的情况下,通…...
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标准库中,std::set 和 std::map都是非常常用的容器,它们提供了基于键值对的存储和快速查找能力。然而,关于它们的底层实现,C标准并没有强制规定具体的数据结构,只…...
计算机网络-PIM协议基础概念
一、PIM基础概念 组播网络回顾: 组播网络从网络结构上大体可以分为三个部分: 源端网络:将组播源产生的组播数据发送至组播网络。 组播转发网络:形成无环的组播转发路径,该转发路径也被称为组播分发树(Multi…...
优化PyCharm:让IDE响应速度飞起来
优化PyCharm:让IDE响应速度飞起来 PyCharm,作为一款功能强大的集成开发环境(IDE),在提供丰富功能的同时,有时也会出现响应慢的问题。这不仅影响开发效率,还可能打击开发者的积极性。本文将详细…...
对象转化为String,String转化为对象
title: 对象转化为string,string转化为对象 date: 2024-08-02 11:50:40 tags: javascript const obj { uname:haha, age:18,gender:女} //将对象转换成string JSON.stringify(obj) //取成一个对象,将字符串传化为对象 JSON.parse(obj)常用领域在localst…...
SolverLearner:提升大模型在高度归纳推理的复杂任务性能,使其能够在较少的人为干预下自主学习和适应
SolverLearner:提升大模型在高度归纳推理的复杂任务性能,使其能够在较少的人为干预下自主学习和适应 提出背景归纳推理(Inductive Reasoning)演绎推理(Deductive Reasoning)反事实推理(Counterf…...
PHP智能问诊导诊平台-计算机毕业设计源码75056
摘 要 智能问诊导诊平台作为一种智能化医疗服务工具,利用PHP语言开发,旨在为用户提供便捷的在线问诊和导诊服务。该平台集成了智能算法和医疗数据,实现了智能化的病情诊断和治疗建议,帮助用户更快速地获取医疗信息和建议。用户可…...
数据结构初阶(c语言)-排序算法
数据结构初阶我们需要了解掌握的几种排序算法(除了直接选择排序,这个原因我们后面介绍的时候会解释)如下: 其中的堆排序与冒泡排序我们在之前的文章中已经详细介绍过并对堆排序进行了一定的复杂度分析,所以这里我们不再过多介绍。 一&#x…...
网络云相册实现--nodejs后端+vue3前端
目录 主页面 功能简介 系统简介 api 数据库表结构 代码目录 运行命令 主要代码 server apis.js encry.js mysql.js upload.js client3 index.js 完整代码 主页面 功能简介 多用户系统,用户可以在系统中注册、登录及管理自己的账号、相册及照片。 每…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

