多线程同步
多线程
程序中默认只有一个线程,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;
}
相关文章:
多线程同步
多线程 程序中默认只有一个线程,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服务器端口(默认为21) ftp_po…...
软件测试 | APP测试 —— Appium 的环境搭建及工具安装教程
大家应该都有同一种感觉,学习appium最大的难处之一在于环境的安装,安装流程比较繁琐,安装的工具和步骤也较多,以下是基于Windows系统下的Android手机端的安装流程。就像我们在用Selenium进行web自动化测试的时候一样,我…...
计算机人工智能前沿进展-大语言模型方向-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服务过程中,可以根据业务需要替换HENGSHI自带的minio。本文讲述使用Aws S3和Aliyun OSS替代衡石minio的过程。 准备工作 在进行配置前,请在aws s3或aliyun oss完成如下准备工作。 创建access_key和secret_acces…...
怎么将几个pdf合成为一个?把几个PDF合并成为一个的8种方法
怎么将几个pdf合成为一个?将多个PDF文件合并成一个整体可以显著提高信息整合的效率,并简化文件的管理与传递。例如,将不同章节的电子书合成一本完整的书籍,或者将多个部门的报告整合成一个统一的文档,可以使处理流程变…...
明明没有程序占用端口,但是启动程序却提示端口无法使用,项目也启动失败
明明没有程序占用端口,但是启动程序却提示端口无法使用,项目也启动失败 win10、端口占用、port、netstat、used背景 曾在springboot中遇到过,新建spring cloud时又遇到这个问题,如果不从根本上解决,就需要改端口&…...
ClickHouse的安装配置+DBeaver远程连接
1、clickhouse的下载: 先去clickhouse官网进行下载,继续往下翻找文档,将DBeaver也下载下来 下载地址: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】 🚀 引言 在计算机科学的世界中,算法 是每一个程序背后的隐形支柱。从简单的排序到复杂的人工智能,算法无处不在。然而,编写一个能运行的程序只是开始,当程序面对庞大的数据集时,算法的效率…...
记忆化搜索专题——算法简介力扣实战应用
目录 1、记忆化搜索算法简介 1.1 什么是记忆化搜索 1.2 如何实现记忆化搜索 1.3 记忆化搜索与动态规划的区别 2、算法应用【leetcode】 2.1 题一:斐波那契数 2.1.1 递归暴搜解法代码 2.1.2 记忆化搜索解法代码 2.1.3 动态规划解法代码 2.2 题二࿱…...
【Java】【力扣】83.删除排序链表中的重复元素
题目 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 示例 1: 输入:head [1,1,2] 输出:[1,2]示例 2: 输入:head [1,1,2,3,3] 输出&#…...
vue3项目实现全局国际化
本文主要梳理vue3项目实现全项目格式化,例如在我前面文章使用若依创建vue3的项目中,地址:若依搭建vue3项目在导航栏中切换,页面中所有的组件的默认语言随之切换,使用的组件库依旧是element-plus,搭配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
🤓😍Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-前端领域博主 🐱🐉若此文你认为写的不错,不要吝啬你的赞扬,求收藏,求评论,求一个大大的赞!👍* &#x…...
开源 AI 智能名片链动 2+1 模式 S2B2C 商城小程序与社交电商的崛起
摘要:本文深入探讨了社交电商迅速发展壮大的原因,并分析了开源 AI 智能名片链动 21 模式 S2B2C 商城小程序在社交电商中的重要作用。通过对传统电商与社交电商的对比,以及对各发展因素的剖析,阐述了该小程序如何为社交电商提供新的…...
在线IP代理检测:保护您的网络安全
在互联网飞速发展的今天,越来越多的人开始意识到网络安全和隐私保护的重要性。在线IP代理检测工具作为一种有效的网络安全手段,能够帮助用户识别和检测IP代理的使用情况,从而更好地保护个人隐私和数据安全。本文将详细介绍在线IP代理检测的相…...
【算法】BFS—解开密码锁的最少次数
题目 一个密码锁由 4 个环形拨轮组成,每个拨轮都有 10 个数字: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 。每个拨轮可以自由旋转:例如把 9 变为 0,0 变为 9 。每次旋转都只能旋转一个拨轮的一位数字。 锁的初始数字为 0000 ,一个…...
非守护线程会阻止JVM的终止吗
非守护线程会阻止JVM的终止。在Java中,线程分为守护线程(Daemon Threads)和非守护线程(Non-Daemon Threads,也被称为用户线程)。这两种线程在JVM终止时表现出不同的行为。 非守护线程是JVM中执行程序主要逻…...
Grafana面板-linux主机详情(使用标签过滤主机监控)
1. 采集器添加labels标签区分业务项目 targets添加labels (模板中使用的project标签) … targets: [‘xxxx:9100’] labels: project: app2targets: [‘xxxx:9100’] labels: project: app1 … 2. grafana面板套用 21902 模板 演示...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...







