学习记录——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;
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 实现改造 GooFlow 流程图插件与数据库应用的结合

目录 关于 GooFlow 功能需求 范例运行环境 设计数据表 流程项目表 流程项目节点明细表 流程项目节点审批人表 人员信息表 示例代码 流程图主功能 设置审批人信息 运行结果演示 总结 关于 GooFlow GooFlow 一个基于 Jquery/FontAwesome 的流程图/架构图画图插件&…

Spring File Storage(文件的对象存储)框架基本使用指南

概述 本文仅作为快速入门&#xff0c;深入学习及使用详见官网 云存储 在开发过程当中&#xff0c;会使用到存文档、图片、视频、音频等等&#xff0c;这些都会涉及存储的问题&#xff0c;文件可以直接存服务器&#xff0c;但需要考虑带宽和存储空间&#xff0c;另外一种方式…

漏洞挖掘 | src中一次证书站有趣的SQL注入

一、确定站点 按照以前文章中提到的寻找可进站测试的思路&#xff0c;找到了某证书站的一处站点&#xff0c;通告栏中写明了初始密码的结构&#xff0c;因此我们可通过信息搜集进入该站点(可以考虑去搜集比较老的学号&#xff0c;因为这样的账号要么被冻结&#xff0c;要么就是…

AMD Product Specifications - AMD 产品规格汇总

AMD Product Specifications - AMD 产品规格汇总 1. Desktop, Laptop and Workstation Processor Specifications (台式处理器、笔记本电脑处理器和工作站处理器规格)2. Server Processor Specifications (服务器处理器规格)3. Embedded Processor Specifications (嵌入式处理器…

土耳其射击运动员尤素夫迪凯克在巴黎奥运会上成为互联网热门人物

这名51岁的男子以自己的方式获得了第二名,这对他的祖国来说是一个历史性的时刻。 这位冷静沉着的土耳其手枪射击运动员周二在 2024 年巴黎奥运会上获得银牌&#xff0c;在网上吸引了众多粉丝。 尤素夫迪克与他的搭档塞夫瓦尔伊莱达塔尔汉在混合团体 10 米气手枪比赛中获得第二…

jupyter notebook安装

1.安装 pip install notebook 2.显示配置文件&#xff1a; jupyter notebook --generate-config 3.修改代码路径&#xff1a; 编辑配置文件C:\Users\a\.jupyterjupyter_notebook_config.py 4.运行 jupyter notebook 会自动弹出http://localhost:8888/tree

QT 笔记

HTTPS SSL配置 下载配置 子父对象 QTimer *timer new QTimer; // QTimer inherits QObject timer->inherits("QTimer"); // returns true timer->inherits("QObject"); // returns true timer->inherits("QAbst…

保形分位数回归(CQR)

目录 简介1 介绍提纲式总结 分位数回归从数据中估计分位数 3 共性预测4 保形分位数回归(CQR)两个定理 6 实验7 结论 简介 保形预测是一种构造在有限样本中获得有效覆盖的预测区间的技术&#xff0c;无需进行分布假设。尽管有这种吸引力&#xff0c;但现有的保形方法可能是不必…

【文心智能体】梗图七夕版,一分钟让你看懂如何优化prompt,以及解析低代码工作流编排实现过程和零代码结合插件实现过程,依然是干货满满,进来康康吧

目录 背景什么是梗图梗图概念梗图结构 低代码开发最小运行单元大模型链提示词模板文心模板输出效果 测试工具链HTTP请求工具 梗图工具链全流程 梗图优化Prompt提示词优化后梗图结构提示词前后对比优化前效果优化后效果API接口BOS图片水印 梗图插件格式说明构思插件清单文件定义…

HTML-07.表格标签

一、要制作的表格如下 二、代码如下 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>表格标签<…

数据结构——双链表详解(超详细)

前言&#xff1a; 小编在之前已经写过单链表的创建了&#xff0c;接下来要开始双链表的讲解了&#xff0c;双链表比单链表要复杂一些&#xff0c;不过确实要比单链表更好进行实现&#xff01;下面紧跟小编的步伐&#xff0c;开启今天的双链表之旅&#xff01; 目录 1.概念和结构…

【已解决】没有密码,如何解除PPT的“只读方式”?

PPT可以设置有密码的“只读方式”&#xff0c;保护文件不被随意编辑更改。 在设置保护后&#xff0c;打开PPT时就会弹出对话框&#xff0c;提示需要“输入密码以修改或以只读方式打开”&#xff0c;也就是输入密码才能编辑修改PPT&#xff0c;如果点击“只读”也能打开文件&am…

[BJDCTF2020]Mark loves cat1

打开题目 发现这么多链接&#xff0c;以为要一点点去找功能上的漏洞。当你源代码&#xff0c;dirsearch&#xff0c;抓包等等操作之后&#xff0c;发现什么都没有。所以这题又是一道源码泄露题&#xff0c;上GItHack。扫描结果如下 http://63f29a80-e08b-43ae-a6d0-8e70fb02ea…

闪耀STIF2023国际科创节,望繁信科技荣获年度行业创新典范奖

2023年12月15日&#xff0c;望繁信科技在STIF2023第四届国际科创节暨DSC2023国际数字服务大会&#xff08;数服会&#xff09;活动评选中&#xff0c;斩获“2023年度行业创新典范”大奖。 作为科技创新与数字化服务领域最具影响力的年度盛会之一&#xff0c;STIF2023国际科创节…

目标检测——YOLOv10: Real-Time End-to-End Object Detection

YOLOv10是在YOLOv8的基础上&#xff0c;借鉴了RT-DETR的一些创新点改进出来的 标题&#xff1a;YOLOv10: Real-Time End-to-End Object Detection论文&#xff1a;https://arxiv.org/pdf/2405.14458源码&#xff1a;https://github.com/THU-MIG/yolov10 1. 论文介绍 在过去的几…

JAVA—面向对象编程高级

学习了一定基础后&#xff0c;开始更加深入的学习面向对象&#xff0c;包含static,final两个关键字&#xff0c;面向对象编程三大特征之继承和多态。以及对于抽象类&#xff0c;内部类&#xff0c;接口&#xff0c;枚举&#xff0c;泛型的学习。 目录 1.static &#xff08;…

原神自定义倒计时

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>原神倒计时</title><style>* {margin: 0;padding: 0;box-sizing: border-box;user-select: none;body {background: #0b1b2c;}}header {…

Javase--Date

1.Date简介 Date的学习: 1. java.util包下的类 2.用于日期、时间的描述 3. 实际上时距离一个固定时间点1970年1月1日00:00:00的毫秒数 4.我们常用的是格林威治时间:GMT UTC:世界调整时间 5.固定时间点:说的其实是本初子午线的时间。因此北京时间是1970年1月1日8:00:…

c++ 容器 vector

vector的意思就是向量&#xff0c;就是一个顺序表的意思&#xff0c;这个顺序表可以存任意的类型&#xff0c;因为其线性的内存特点&#xff0c;所以在stl里是经常被使用的存在。 vector vector既然要能储存任意的变量&#xff0c;那么就必须使用模板: 这里的T就是变量类型&a…

微信小程序之behaviors

目录 概括 Demo演示 进阶演示 1. 若具有同名的属性或方法 2. 若有同名的数据 3. 若有同名的生命周期函数 应用场景 最后 属性&方法 组件中使用 代码示例&#xff1a; 同名字段的覆盖和组合规则 概括 一句话总结: behaviors是用于组件间代码共享的特性, 类似一…