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

多线程同步

多线程

程序中默认只有一个线程,pthread_create()函数调用后就有2个线程。

pthread_create()

#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
using namespace std;
//线程函数
void * callback(void * arg) {cout <<"pthread arg:"<< *(int *)arg << endl;return NULL;
}
int main() {pthread_t tid;// 创建一个子线程int num = 10;//传入参数int ret = pthread_create(&tid, NULL, callback, (void *)&num);if(ret != 0) {//不等于0说明失败了char * errstr = strerror(ret);cout << errstr << endl;}//主进程运行cout << "child pthread:" << tid << endl;cout << "main pthread:" << pthread_self() << endl;for (int i = 0; i < 50; i++){cout << i << endl;}//主线程退出不会影响其他进程pthread_exit(NULL);return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

pthread_exit()是退出线程

pthread_join()

子线程的资源可以由任意线程通过:pthread_join()回收资源,一般由主线程负责回收。

#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
using namespace std;
//线程函数
int val = 10;//全局变量
void *callback(void *arg)
{cout << "child thread:" << pthread_self() << endl;// 返回值是void*,作用相当returnpthread_exit((void *)&val);
}
int main() {pthread_t tid;// 创建一个子线程pthread_create(&tid, NULL, callback, NULL);//主进程运行for (int i = 0; i < 10; i++){cout << i << endl;}//主线程回收子线程资源//返回值是二级指针int *pth_ret;pthread_join(tid, (void **)&pth_ret);cout << "exit data:" << *pth_ret << endl;pthread_exit(NULL);return 0;
}

pthread_join()是一个阻塞函数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用全局变量才能保持值稳定

pthread_death()

分离一个线程,被分离的线程的资源会被系统自动释放

  • 不可以多次分离线程
  • 不能分离一个已经分离的线程
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
using namespace std;
//线程函数
void* callback(void *arg){pthread_exit(NULL);
}
int main() {pthread_t tid;// 创建一个子线程pthread_create(&tid, NULL, callback, NULL);// 输出线程号cout << "main:" << pthread_self() << ",child:" << tid << endl;//设置子线程分离pthread_detach(tid);//释放主线程pthread_exit(NULL);return 0;
}

pthread_cancel()

向线程发送一个取消的线程,说白了就是终止线程

不是立即终止,而是执行到某个取消点才会终止

  • 取消点是系统规定好的一些系统调用
  • 可以把取消点理解为从用户区到内核区的切换点
//线程函数
void* callback(void *arg){for (int i = 0; i < 6;i++){cout << "child pthread:" << i << endl;}return NULL;
}
int main() {pthread_t tid;// 创建一个子线程pthread_create(&tid, NULL, callback, NULL);// 输出线程号cout << "main:" << pthread_self() << ",child:" << tid << endl;/*取消线程*/pthread_cancel(tid);for (int i = 0; i < 6; i++){cout << "main pthread:" << i << endl;}// 释放主线程pthread_exit(NULL);return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

说明cancel的线程不是立即取消的

线程属性attr

//线程属性类型 pthread_attr_t
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

还有很多的线程属性相关函数

//线程函数
void* callback(void *arg){for (int i = 0; i < 6;i++){cout << "child pthread:" << i << endl;}return NULL;
}
int main() {//创建线程属性变量pthread_attr_t attr;//初始化线程属性pthread_attr_init(&attr);//设置线程属性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);pthread_t tid;//第二个参数是自己设置好的线程属性pthread_create(&tid, &attr, callback, NULL);cout << "main:" << pthread_self() << ",child:" << tid << endl;//释放线程属性资源pthread_attr_destroy(&attr);pthread_exit(NULL);return 0;
}

获取栈的大小

int main() {//创建线程属性变量pthread_attr_t attr;//初始化线程属性pthread_attr_init(&attr);//设置线程属性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);pthread_t tid;//第二个参数是自己设置好的线程属性pthread_create(&tid, &attr, callback, NULL);//获取栈的大小size_t size;pthread_attr_getstacksize(&attr,&size);cout << "pthread size:" << size << endl;// 释放线程属性资源pthread_attr_destroy(&attr);pthread_exit(NULL);return 0;
}

线程同步

多线程编程的重点

#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
using namespace std;
/* 三个窗口一起卖100张票 */
//线程函数是卖票
int ticket = 100;//全局
void* sellTicket(void* arg){usleep(100);while (ticket > 0){cout << pthread_self() << "在卖第" << ticket << "张票" << endl;ticket--;}return NULL;
}
int main() {//创建3个子线程,主线程只进行线程资源回收pthread_t tid1, tid2, tid3;pthread_create(&tid1, NULL, sellTicket, NULL);pthread_create(&tid2, NULL, sellTicket, NULL);pthread_create(&tid3, NULL, sellTicket, NULL);//回收子线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);//退出主线程pthread_exit(NULL);return 0;
}

线程的优势就是可以通过全局变量共享信息,不过共享就存在了竞争

多个线程不会同时修改同一变量;某一线程不会读取其他线程正在修改的变量

这个代码的问题在于:

同一张票可以被多个线程卖;线程执行时间问题导致买了负票

临界资源

临界区是指访问某一共享资源的代码片段,并且这段代码的执行应为原子操作,也就是同时访问同一共享资源的其他线程不应终端该片段的执行。

线程同步

即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作.直到该线程完成操作,其他线程才能对该内存地址进行操作,而其他线程则处于等待状态。

线程同步影响并发效率,但是为了数据安全是必须的

原子操作就是执行一段代码只能完整执行,不能被中断,防止并发执行带来的问题

互斥锁

mutex--------- mutual exclusion的缩写

  • 确保某时刻仅有一个线程可以访问某项共享资源。
  • 可以使用互斥量来保证对任意共享资源的原子访问。

互斥量有两种状态

  • 已锁定(locked)
  • 未锁定(unlocked)

每一线程在访问同一资源时将采用如下协议:

  • 针对共享资源锁定互斥量
  • 访问共享资源
  • 对互斥量解锁

只有一个线程能对共享资源加锁,其他线程加锁会被阻塞

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


//互斥量的类型 
pthread_mutex_t  mt;
//相关函数
//restrict--c语言修饰符
int pthread_mutex_init(pthread_mutex_t *restrict mutex, constpthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

代码使用:

#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
using namespace std;
/* 三个窗口一起卖1000张票 */
int ticket = 1000;//全局
/*互斥量:全局变量*/
pthread_mutex_t mutex;
void *sellTicket(void *arg){while(1){/*加锁*/pthread_mutex_lock(&mutex);if (ticket > 0){usleep(200);cout << pthread_self() << "在卖第" << ticket << "张票" << endl;ticket--;}else{/*解锁*/pthread_mutex_unlock(&mutex);break;}//解锁pthread_mutex_unlock(&mutex);}return NULL;
}
int main() {//初始化互斥量pthread_mutex_init(&mutex, NULL);// 创建3个子线程,主线程只进行线程资源回收pthread_t tid1, tid2, tid3;pthread_create(&tid1, NULL, sellTicket, NULL);pthread_create(&tid2, NULL, sellTicket, NULL);pthread_create(&tid3, NULL, sellTicket, NULL);//回收子线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);//退出主线程pthread_exit(NULL);//释放互斥量pthread_mutex_destroy(&mutex);return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

死锁

什么是死锁

两个线程都在等待对方释放锁,在没有外力的作用下,这些线程会一直相互等待,就没办法继续运行,这种情况就是发生了死锁

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

线程B锁住了资源2但是等待线程A释放资源1

线程A锁住资源1同时又在等待线程B释放资源2

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

死锁发生的4个条件

  • 互斥条件

    多个线程不能同时使用同一个资源

  • 持有并等待条件

    线程 在等待资源 1的同时并不会释放自己已经持有的资源 2

  • 不可剥夺条件

    当线程已经持有了资源 ,在自己使用完之前不能被其他线程获取

  • 环路等待条件

    在死锁发生的时候,两个线程获取资源的顺序构成了环形链

死锁的例子

#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
using namespace std;
//初始化两个锁
pthread_mutex_t mutex1, mutex2;
//线程函数
void* workA(void* arg){pthread_mutex_lock(&mutex1);sleep(1);pthread_mutex_lock(&mutex2);cout << "workA,,,," << endl;pthread_mutex_unlock(&mutex2);pthread_mutex_unlock(&mutex1);return NULL;
}
void* workB(void* arg){pthread_mutex_lock(&mutex2);sleep(1);pthread_mutex_lock(&mutex1);cout << "workB,,,," << endl;pthread_mutex_unlock(&mutex1);pthread_mutex_unlock(&mutex2);return NULL;
}
int main(){//初始化锁pthread_mutex_init(&mutex1,NULL);pthread_mutex_init(&mutex2, NULL);//创建两个线程pthread_t tid1, tid2;pthread_create(&tid1, NULL,workA, NULL);pthread_create(&tid1, NULL,workB, NULL);//退出主线程pthread_exit(NULL);//释放两把锁pthread_mutex_destroy(&mutex1);pthread_mutex_destroy(&mutex2);return 0;
}

程序卡死

读写锁

实际上多个线程同时读访问共享资源并不会导致问题。

在对数据的读写操作中,更多的是读操作,写操作较少

读写锁的特点

  • 如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作

  • 如果有其它线程写数据,则其它线程都不允许读、写操作

  • 写是独占的,写的优先级高

    同样有读锁和写锁请求资源,优先满足写

//读写锁的类型 
pthread_rwlock_t
//函数
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

读写锁也是一把锁,只是可以指定锁类型

#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
using namespace std;
/*8个线程,3个写,5个读*/
//共享数据
int nums = 100;
// 初始化锁
pthread_rwlock_t rw;
//线程函数
void* writeNum(void* arg){while(1){pthread_rwlock_wrlock(&rw);nums++;cout << pthread_self() << "is write,nums=" << nums << endl;pthread_rwlock_unlock(&rw);usleep(100);}return NULL;
}
void* readNum(void* arg){while(1){pthread_rwlock_rdlock(&rw);cout << pthread_self() << "is read,nums=" << nums << endl;pthread_rwlock_unlock(&rw);usleep(100);}return NULL;
}
int main(){//初始化锁pthread_rwlock_init(&rw,NULL);// 创建线程pthread_t wtid[3],rtid[5];for (int i = 0; i < 3;i++)pthread_create(&wtid[i], NULL, writeNum, NULL);for (int i = 0; i < 5;i++)pthread_create(&rtid[i], NULL, readNum, NULL);//设置线程分离for (int i = 0; i < 3;i++)pthread_detach(wtid[i]);for (int i = 0; i < 5;i++)pthread_detach(rtid[i]);pthread_exit(NULL);//释放锁pthread_rwlock_destroy(&rw);return 0;
}

生产者消费者模型

什么是生产者-消费者模式

比如有两个进程A和B,它们共享一个固定大小的缓冲区,A进程产生数据放入缓冲区,B进程从缓冲区中取出数据进行计算,那么这里其实就是一个生产者和消费者的模式,A相当于生产者,B相当于消费者

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该模式的对象:

  • 生产者
  • 消费者
  • 缓冲区

为了达到生产者和消费者生产数据和消费数据之间的平衡,那么就需要一个缓冲区用来存储生产者生产的数据

生产者-消费者模式的特点

  • 保证生产者不会在缓冲区满的时候继续向缓冲区放入数据,而消费者也不会在缓冲区空的时候,消耗数据
  • 当缓冲区满的时候,生产者会进入休眠状态,当下次消费者开始消耗缓冲区的数据时,生产者才会被唤醒,开始往缓冲区中添加数据;
  • 当缓冲区空的时候,消费者也会进入休眠状态,直到生产者往缓冲区中添加数据时才会被唤醒
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
using namespace std;
//创建一个互斥量
pthread_mutex_t mutex;
/*生产者消费者模型*/
struct node{int num;node *next;node(int val,node* next=nullptr):num(val),next(next){}
};
node *head = NULL;
void *product(void *)
{//不断创建新节点到链表中while(1){pthread_mutex_lock(&mutex);node *newNode = new node(rand() % 100, head);head = newNode;cout << pthread_self << " create node" << newNode->num << endl;pthread_mutex_unlock(&mutex);usleep(100);}return NULL;
}
void* custom(void*){//不断取出头节点while(1){pthread_mutex_lock(&mutex);node *tmp = head;if(head){head = head->next;cout << pthread_self() << " consume node" << tmp->num << endl;delete (tmp);pthread_mutex_unlock(&mutex);usleep(100);}else{pthread_mutex_unlock(&mutex);}}return NULL;
}
int main(){//创建互斥量pthread_mutex_init(&mutex,NULL);// 创建5个生产者和5个消费者pthread_t pTids[5], cTids[5];for (int i = 0; i < 5;i++){pthread_create(&pTids[i], NULL, product, NULL);pthread_create(&cTids[i], NULL, custom, NULL);}//设置分离for (int i = 0; i < 5;i++){pthread_detach(pTids[i]);pthread_detach(cTids[i]);}/*保证程序执行*/while(1){sleep(10);}//释放互斥量pthread_mutex_destroy(&mutex);// 退出主线程pthread_exit(NULL);return 0;
}

有可能发生死锁,没有解决消费者没数据要通知生产者生产数据

条件变量

condition variables

不是锁,只是阻塞和解除阻塞

  • 分为一直等待和限时等待

    wait,timewait

  • 可以唤醒1个或者多个线程

    signal一个,broadcast多个

//条件变量的类型 
//pthread_cond_t
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t*restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t*restrict mutex, const struct timespec *restrict abstime);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

加了条件变量

#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
using namespace std;
pthread_mutex_t mutex;
/*创建条件变量*/
pthread_cond_t cond;
struct node
{int num;node *next;node(int val,node* next=nullptr):num(val),next(next){}
};
node *head = NULL;
void *product(void *){while(1){pthread_mutex_lock(&mutex);node *newNode = new node(rand() % 100, head);head = newNode;cout << pthread_self << " create node" << newNode->num << endl;/*只要生产了,就通知消费者消费*/pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);usleep(100);}return NULL;
}
void* custom(void*){while(1){pthread_mutex_lock(&mutex);node *tmp = head;if(head){head = head->next;cout << pthread_self() << " consume node" << tmp->num << endl;delete (tmp);pthread_mutex_unlock(&mutex);usleep(100);}else{/*没有数据,等待*///阻塞会释放互斥锁pthread_cond_wait(&cond, &mutex);pthread_mutex_unlock(&mutex);//没数据也要释放,不然死锁}}return NULL;
}
int main(){pthread_mutex_init(&mutex,NULL);/*创建条件变量*/pthread_cond_init(&cond, NULL);pthread_t pTids[5], cTids[5];for (int i = 0; i < 5;i++){pthread_create(&pTids[i], NULL, product, NULL);pthread_create(&cTids[i], NULL, custom, NULL);}for (int i = 0; i < 5;i++){pthread_detach(pTids[i]);pthread_detach(cTids[i]);}while(1){sleep(10);}pthread_mutex_destroy(&mutex);/*释放条件变量*/pthread_cond_destroy(&cond);pthread_exit(NULL);return 0;
}

信号量

信号量是一个计数器,用于控制多个进程对共享资源的访问。

信号量是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。

//信号量的类型 
//sem_t
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem, int *sval);
  • 初始化:第二个变量区分进程和线程(0是线程)
  • wait阻塞,把信号量值-1
  • post,把信号量值+1,唤醒wait
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
#include <semaphore.h>
using namespace std;
pthread_mutex_t mutex;
//创建2个信号量
sem_t psem, csem;
struct node
{int num;node *next;node(int val,node* next=nullptr):num(val),next(next){}
};
node *head = NULL;
void *product(void *){while(1){//生产者-1sem_wait(&psem);pthread_mutex_lock(&mutex);node *newNode = new node(rand() % 100, head);head = newNode;cout << pthread_self << " create node" << newNode->num << endl;pthread_mutex_unlock(&mutex);//消费者+1sem_post(&csem);usleep(100);}return NULL;
}
void* custom(void*){while(1){//消费者-1sem_wait(&csem);pthread_mutex_lock(&mutex);node *tmp = head;head = head->next;cout << pthread_self() << " consume node" << tmp->num << endl;delete (tmp);pthread_mutex_unlock(&mutex);//生产者+1sem_post(&psem);}return NULL;
}
int main(){pthread_mutex_init(&mutex,NULL);/*创建信号量*/sem_init(&psem, 0, 8);sem_init(&csem, 0, 0);pthread_t pTids[5], cTids[5];for (int i = 0; i < 5;i++){pthread_create(&pTids[i], NULL, product, NULL);pthread_create(&cTids[i], NULL, custom, NULL);}for (int i = 0; i < 5;i++){pthread_detach(pTids[i]);pthread_detach(cTids[i]);}while(1){sleep(10);}pthread_mutex_destroy(&mutex);/*释放信号量*/sem_destroy(&psem);sem_destroy(&csem);pthread_exit(NULL);return 0;
}

相关文章:

多线程同步

多线程 程序中默认只有一个线程&#xff0c;pthread_create()函数调用后就有2个线程。 pthread_create() #include <pthread.h> #include <string.h> #include <unistd.h> #include <iostream> using namespace std; //线程函数 void * callback(vo…...

第159天:安全开发-Python-协议库爆破FTPSSHRedisSMTPMYSQL等

案例一: Python-文件传输爆破-ftplib 库操作 ftp 协议 开一个ftp 利用ftp正确登录与失败登录都会有不同的回显 使用ftplib库进行测试 from ftplib import FTP # FTP服务器地址 ftp_server 192.168.172.132 # FTP服务器端口&#xff08;默认为21&#xff09; ftp_po…...

软件测试 | APP测试 —— Appium 的环境搭建及工具安装教程

大家应该都有同一种感觉&#xff0c;学习appium最大的难处之一在于环境的安装&#xff0c;安装流程比较繁琐&#xff0c;安装的工具和步骤也较多&#xff0c;以下是基于Windows系统下的Android手机端的安装流程。就像我们在用Selenium进行web自动化测试的时候一样&#xff0c;我…...

计算机人工智能前沿进展-大语言模型方向-2024-09-13

计算机人工智能前沿进展-大语言模型方向-2024-09-13 1. OneEdit: A Neural-Symbolic Collaboratively Knowledge Editing System Authors: Ningyu Zhang, Zekun Xi, Yujie Luo, Peng Wang, Bozhong Tian, Yunzhi Yao, Jintian Zhang, Shumin Deng, Mengshu Sun, Lei Liang, Z…...

衡石分析平台使用手册-替换衡石minio

替换衡石minio​ 在使用HENGSHI SENSE服务过程中&#xff0c;可以根据业务需要替换HENGSHI自带的minio。本文讲述使用Aws S3和Aliyun OSS替代衡石minio的过程。 准备工作​ 在进行配置前&#xff0c;请在aws s3或aliyun oss完成如下准备工作。 创建access_key和secret_acces…...

怎么将几个pdf合成为一个?把几个PDF合并成为一个的8种方法

怎么将几个pdf合成为一个&#xff1f;将多个PDF文件合并成一个整体可以显著提高信息整合的效率&#xff0c;并简化文件的管理与传递。例如&#xff0c;将不同章节的电子书合成一本完整的书籍&#xff0c;或者将多个部门的报告整合成一个统一的文档&#xff0c;可以使处理流程变…...

明明没有程序占用端口,但是启动程序却提示端口无法使用,项目也启动失败

明明没有程序占用端口&#xff0c;但是启动程序却提示端口无法使用&#xff0c;项目也启动失败 win10、端口占用、port、netstat、used背景 曾在springboot中遇到过&#xff0c;新建spring cloud时又遇到这个问题&#xff0c;如果不从根本上解决&#xff0c;就需要改端口&…...

ClickHouse的安装配置+DBeaver远程连接

1、clickhouse的下载&#xff1a; 先去clickhouse官网进行下载&#xff0c;继续往下翻找文档&#xff0c;将DBeaver也下载下来 下载地址&#xff1a;https://packages.clickhouse.com/rpm/stable/ 下载这个四个rpm包 2、上传rmp文件到Linux中 自己创建的一个clickhouse-ins…...

UVM仿真的运行(四)—— objection 机制

目录 0. 引言 1. uvm_phase::execute_phase line 1432~1470 2. uvm_objection 2.1 get_objection_total 2.2 raise_objection 2.3 drop_objection 2.4 m_execute_scheduled_forks 2.5 wait_for 3. 小结 0. 引言 前面介绍了uvm仿真的启动,按照domain中指定的DAG的pha…...

【ShuQiHere】算法分析:揭开效率与复杂度的神秘面纱

【ShuQiHere】 &#x1f680; 引言 在计算机科学的世界中&#xff0c;算法 是每一个程序背后的隐形支柱。从简单的排序到复杂的人工智能&#xff0c;算法无处不在。然而&#xff0c;编写一个能运行的程序只是开始&#xff0c;当程序面对庞大的数据集时&#xff0c;算法的效率…...

记忆化搜索专题——算法简介力扣实战应用

目录 1、记忆化搜索算法简介 1.1 什么是记忆化搜索 1.2 如何实现记忆化搜索 1.3 记忆化搜索与动态规划的区别 2、算法应用【leetcode】 2.1 题一&#xff1a;斐波那契数 2.1.1 递归暴搜解法代码 2.1.2 记忆化搜索解法代码 2.1.3 动态规划解法代码 2.2 题二&#xff1…...

【Java】【力扣】83.删除排序链表中的重复元素

题目 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2]示例 2&#xff1a; 输入&#xff1a;head [1,1,2,3,3] 输出&#…...

vue3项目实现全局国际化

本文主要梳理vue3项目实现全项目格式化&#xff0c;例如在我前面文章使用若依创建vue3的项目中&#xff0c;地址&#xff1a;若依搭建vue3项目在导航栏中切换&#xff0c;页面中所有的组件的默认语言随之切换&#xff0c;使用的组件库依旧是element-plus&#xff0c;搭配vue-i1…...

Oracle 19c异常恢复—ORA-01209/ORA-65088---惜分飞

由于raid卡bug故障,导致文件系统异常,从而使得数据库无法正常启动,客户找到我之前已经让多人分析,均未恢复成功,查看alert日志,发现他们恢复的时候尝试resetlogs库,然后报ORA-600 kcbzib_kcrsds_1错误 2024-09-15T17:07:32.55321508:00 alter database open resetlogs 2024-09-…...

【Webpack--000】了解Webpack

&#x1f913;&#x1f60d;Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-前端领域博主 &#x1f431;‍&#x1f409;若此文你认为写的不错&#xff0c;不要吝啬你的赞扬&#xff0c;求收藏&#xff0c;求评论&#xff0c;求一个大大的赞&#xff01;&#x1f44d;* &#x…...

开源 AI 智能名片链动 2+1 模式 S2B2C 商城小程序与社交电商的崛起

摘要&#xff1a;本文深入探讨了社交电商迅速发展壮大的原因&#xff0c;并分析了开源 AI 智能名片链动 21 模式 S2B2C 商城小程序在社交电商中的重要作用。通过对传统电商与社交电商的对比&#xff0c;以及对各发展因素的剖析&#xff0c;阐述了该小程序如何为社交电商提供新的…...

在线IP代理检测:保护您的网络安全

在互联网飞速发展的今天&#xff0c;越来越多的人开始意识到网络安全和隐私保护的重要性。在线IP代理检测工具作为一种有效的网络安全手段&#xff0c;能够帮助用户识别和检测IP代理的使用情况&#xff0c;从而更好地保护个人隐私和数据安全。本文将详细介绍在线IP代理检测的相…...

【算法】BFS—解开密码锁的最少次数

题目 一个密码锁由 4 个环形拨轮组成&#xff0c;每个拨轮都有 10 个数字&#xff1a; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 。每个拨轮可以自由旋转&#xff1a;例如把 9 变为 0&#xff0c;0 变为 9 。每次旋转都只能旋转一个拨轮的一位数字。 锁的初始数字为 0000 &#xff0c;一个…...

非守护线程会阻止JVM的终止吗

非守护线程会阻止JVM的终止。在Java中&#xff0c;线程分为守护线程&#xff08;Daemon Threads&#xff09;和非守护线程&#xff08;Non-Daemon Threads&#xff0c;也被称为用户线程&#xff09;。这两种线程在JVM终止时表现出不同的行为。 非守护线程是JVM中执行程序主要逻…...

Grafana面板-linux主机详情(使用标签过滤主机监控)

1. 采集器添加labels标签区分业务项目 targets添加labels &#xff08;模板中使用的project标签&#xff09; … targets: [‘xxxx:9100’] labels: project: app2targets: [‘xxxx:9100’] labels: project: app1 … 2. grafana面板套用 21902 模板 演示...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...