Linux系统编程系列之线程池
Linux系统编程系列(16篇管饱,吃货都投降了!)
1、Linux系统编程系列之进程基础
2、Linux系统编程系列之进程间通信(IPC)-信号
3、Linux系统编程系列之进程间通信(IPC)-管道
4、Linux系统编程系列之进程间通信-IPC对象
5、Linux系统编程系列之进程间通信-消息队列
6、Linux系统编程系列之进程间通信-共享内存
7、Linux系统编程系列之进程间通信-信号量组
8、Linux系统编程系列之守护进程
9、Linux系统编程系列之线程
10、Linux系统编程系列之线程属性
11、Linux系统编程系列之互斥锁和读写锁
12、Linux系统编程系列之线程的信号处理
13、Linux系统编程系列之POSIX信号量
14、Linux系统编程系列之条件变量
15、Linux系统编程系列之死锁
16、 Linux系统编程系列之线程池
一、什么是线程池
线程池就是将许多线程,放置在一个池子中(实际就是一个结构体),只要有任务,就将任务投入池中,这些线程们通过某些机制,及时处理这些任务,一旦处理完后又重新回到池子中重新接收新任务。为了便于管理,线程池还应当提供诸如初始化线程池,增删线程数量,检测未完成任务的数据,检测正在执行任务的线程的数量、销毁线程池等等基本操作。
二、特性
1、更好的满足并发性需求
2、更加节省系统资源,降低负载
3、避免每次需要执行任务时都创建新的线程
4、可以限制并发线程数量,帮助控制应用程序的性能和稳定性
核心思想:通过精巧的设计使得池子中的线程数量可以动态地发生变化,让线程既可以应对并发性需求,又不会浪费系统资源
三、使用场景
用来应对某种场景,要在程序中创建大量线程,并且这些线程的数量和生命周期均不确定,可能方生方死,也可能常驻内存,请看下面举例。
1、处理大量密集型任务
线程池可以处理多个任务,从而减少了创建和销毁线程的开销,提高了系统性能
2、服务端应用程序
线程池可以帮助服务端应用程序同时处理多个客户端请求
3、I/O 密集型应用程序
线程池可以处理 I/O 操作,允许系统使用空闲时间进行其他任务处理
4、Web 应用程序
线程池可以处理来自 Web 客户端的请求,从而提高了 Web 应用程序的性能
总之,任何需要处理大量任务或需要同时处理多个请求的应用程序都可以使用线程池来提高性能和效率。
四、设计思想
设计某个东西一定要有个目标,这里是要求设计一个线程池,首先要明白线程池的作用,然后根据其作用来展开设计。线程池是通过让线程的数量进行动态的变化来及时处理接受的任务,所以核心就是线程和任务。可以将问题转换为如何组织线程和组织任务?借鉴别人的一幅图
1、如何组织线程
从上图可以看出,线程被创建出来之后,都处于睡眠态,它们实际上是进入了条件变量的等待队列中,而任务都被放入一个链表,被互斥锁保护起来
线程的生命周期如下:(这里省略程序执行的流程图)
(1)、线程被创建
(2)、预防死锁,准备好退出处理函数,防止在持有一把锁的状态中死去
(3)、尝试持有互斥锁(等待任务)
(4)、判断是否有任务,若无则进入条件变量等待队列睡眠,若有则进入第(5)步
(5)、从任务链表中取得一个任务
(6)、释放互斥锁
(7)、弹出退出处理函数,避免占用内存
(8)、执行任务
(9)、执行任务完成后重新回到第(2)步
线程的生命周期其实就是线程要做的事情,把上面的第(2)步到第(8)步写成一个线程的例程函数。
2、如何组织任务
所谓的任务就是一个函数,回想一下创建一条线程时,是不是要指定一个线程例程函数。这里的线程池做法是将函数(准确的说是函数指针)及其参数存入一个任务节点,并将节点链接成一个链表。所以现在的问题变成如何设计一个任务链表?
一个链表的操作有:创建节点,增加节点,删除节点,查询节点这几步。由于这里是任务节点,就不需要查询了,当然也可以查询。所以现在的问题转换为任务节点如何设计?
任务节点的数据域主要有函数指针和函数参数,指针域就是一个简单任务结点指针,也可以是双向循环链表。
设计分析到这里,基本上有了框架了,前面买了这么多关子,下面就来个重头戏。其实前面主要是想描述一种设计思想,遇到新的东西如何设计它?首先要明白设计的目的和作用,然后找出核心点,最后就是把核心点进行不断的分析推理,一步步转换为最基本的问题。学过项目管理的同学应该听过在范围管理里有个叫工作分解结构的东西,这里跟那个差不多。
五、案例
实现一个线程池,并完成线程池的基本操作演示
thread_pool.h(线程池头文件)
#ifndef _THREAD_POOL_H #define _THREAD_POOL_H#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <pthread.h>#define MAX_WAITING_TASKS 1000 // 最大的等待任务数量 #define MAX_ACTIVE_THREADS 20 // 最多的活跃线程数量// 任务节点 typedef struct task {void *(*do_task) (void *arg); // 函数指针void *arg; // 函数参数struct task *next; // 指向下一个任务节点 }task;// 线程池的管理节点 typedef struct thread_pool {pthread_mutex_t task_list_lock; // 任务列表互斥锁,保证同一个时间只有一条线程进行访问pthread_cond_t task_list_cond; // 任务列表条件变量,保证没有任务时,线程进入睡眠task *task_list; // 任务列表pthread_t *tids; // 存放线程号的数组int shutdown; // 线程池销毁的开关unsigned int max_waiting_tasks; // 最大等待的任务数量unsigned int waiting_tasks; // 正在等待被处理的任务数量unsigned int active_threads; // 正在活跃的线程数}thread_pool;// 初始化线程池 int init_pool(thread_pool *pool, unsigned int threads_number);// 往线程池中添加任务 int add_task(thread_pool *pool, void *(*do_task)(void *arg), void *arg);// 往线程池中添加线程 int add_thread(thread_pool *pool, unsigned int additional_threads);// 从线程池中删除线程 int remove_thread(thread_pool *pool, unsigned int removing_threads);// 查询线程池中线程数量 int total_thread(thread_pool *pool);// 销毁线程池 int destroy_pool(thread_pool *pool);// 线程的例程函数 void *routine(void *arg);#endif // _THREAD_POOL_H
thread_pool.c(线程池实现文件)
#include "thread_pool.h"// 初始化线程池 int init_pool(thread_pool *pool, unsigned int threads_number) {if(threads_number < 1){printf("warning: threads_number must bigger than 0\n");return -1;}if(threads_number > MAX_ACTIVE_THREADS){printf("warning: threads number is bigger than MAX_ACTIVE_THREADS(%u)\n", MAX_ACTIVE_THREADS);return -1;}pthread_mutex_init(&pool->task_list_lock, NULL); // 初始化互斥锁pthread_cond_init(&pool->task_list_cond, NULL); // 初始化条件变量pool->task_list = calloc(1, sizeof(task)); // 申请一个任务头节点if(!pool->task_list){printf("error: task_list calloc fail\n");return -1;}pool->task_list->next = NULL; // 让任务头节点指向NULLpool->tids = calloc(MAX_ACTIVE_THREADS, sizeof(pthread_t)); // 申请堆数组用来存放线程号if(!pool->tids){printf("error: tids calloc fail\n");return -1;}pool->shutdown = 0; // 关闭线程池销毁开发pool->waiting_tasks = 0; // 当前等待任务为0pool->max_waiting_tasks = MAX_WAITING_TASKS; // 初始化最大等待任务数量pool->active_threads = threads_number; // 初始化活跃的线程数// 根据活跃的线程数,创建对应数量的线程for(int i = 0; i < pool->active_threads; i++){// 需要判断线程是否创建失败// 线程号用线程号数组,线程属性设置为默认的,例程函数是routine,函数参数是线程池errno = pthread_create(&pool->tids[i], NULL, routine,(void*)pool);if(errno != 0){perror("error: pthread_create fail");return -1;}}return 1; }// 往线程池中添加任务 int add_task(thread_pool *pool, void *(*do_task)(void *arg), void *arg) {if(!pool){printf("warning: thread_pool is null\n");return -1;}// 如果当前等待执行的任务数超过最大等待的任务数量,则退出if(pool->waiting_tasks >= pool->max_waiting_tasks){printf("warning: task_list is full, too many list\n");return -1;}// 尝试给新的任务节点申请空间task *new_task = (task*)malloc(sizeof(task));if(!new_task){printf("error: task malloc fail\n");return -1;}// 初始化任务节点new_task->do_task = do_task;new_task->arg = arg;new_task->next = NULL;// 把任务节点添加到任务列表最后面// 1、先找到任务列表末尾task *tmp = NULL;for(tmp = pool->task_list; tmp->next; tmp = tmp->next);// 2、上锁pthread_mutex_lock(&pool->task_list_lock);// 3、添加新任务到任务列表末尾,同时当前等待任务数+1tmp->next = new_task;pool->waiting_tasks++;// 4、解锁pthread_mutex_unlock(&pool->task_list_lock);// 唤醒一个正在条件变量中睡眠等待的线程,取执行任务pthread_cond_signal(&pool->task_list_cond);return 1; }// 往线程池中添加线程,返回实际添加的线程数量 int add_thread(thread_pool *pool, unsigned int additional_threads) {if(!pool){printf("warning: thread_pool is null\n");return -1;}if(additional_threads == 0){return 0;}// 期望活跃的线程数 = 目前活跃的线程数 + 期望添加的线程数unsigned int total_threads = pool->active_threads + additional_threads;// 如果超过最大的活跃线程数就打印警告if(total_threads > MAX_ACTIVE_THREADS){printf("warning: add too many threads\n");}int actual_add_threads = 0;for(int i = pool->active_threads; i < total_threads && i < MAX_ACTIVE_THREADS; i++){// 需要判断线程是否创建失败// 线程号用线程号数组,线程属性设置为默认的,例程函数是routine,函数参数是线程池errno = pthread_create(&pool->tids[i], NULL, routine,(void*)pool);if(errno != 0){perror("error: pthread_create fail");// 如果一个都没有创建成功,就直接返回if(actual_add_threads == 0){return -1;}// 如果成功创建了多个线程,但是本次创建失败,就跳出循环break;}else{actual_add_threads++; // 线程创建成功就+1}}// 更新线程池中活跃的线程数pool->active_threads += actual_add_threads;return actual_add_threads; // 返回实际添加的线程 }// 从线程池中删除线程,返回实际删除线程的数量 int remove_thread(thread_pool *pool, unsigned int removing_threads) {if(!pool){printf("warning: thread_pool is null\n");return -1;}if(removing_threads == 0){return pool->active_threads;}// 如果要删除的线程数大于线程池中活跃的线程数,则打印警告if(removing_threads >= pool->active_threads){printf("warning: remove too many threads\n");}// 剩余数量 = 活跃数量 - 删除的目标数 int remaining_threads = pool->active_threads - removing_threads;// 目的是为了让线程池中最少保留有一个线程可以用于执行任务remaining_threads = remaining_threads > 0 ? remaining_threads : 1;int actual_remove_threads = 0;;// 循环取消线程直到等于期望线程数for(int i = pool->active_threads-1; i > remaining_threads-1; i--){errno = pthread_cancel(pool->tids[i]);if(errno != 0){printf("[%ld] cancel error: %s\n", pool->tids[i], strerror(errno));break;}else{actual_remove_threads++;}}// 更新线程池中活跃的线程数量pool->active_threads -= actual_remove_threads;return actual_remove_threads; // 返回实际删除的线程 }// 查询线程池中线程数量 int total_thread(thread_pool *pool) {return pool->active_threads; }// 销毁线程池 int destroy_pool(thread_pool *pool) {if(!pool){printf("warning: thread_pool is null\n");return -1;}pool->shutdown = 1; // 启动线程池销毁开关pthread_cond_broadcast(&pool->task_list_cond); // 唤醒所有在线程池中的线程// 循环等待所有线程退出for(int i = 0; i < pool->active_threads; i++){errno = pthread_join(pool->tids[i], NULL);if(errno != 0){printf("join tids[%d] error: %s\n", i, strerror(errno));}else{printf("[%ld] is joined\n", pool->tids[i]);}}// 头删法,从头一个个的删除任务节点for(task *p = pool->task_list->next; p; p = pool->task_list){pool->task_list->next = p->next; // 修改首元节点free(p);}free(pool->task_list);free(pool->tids);free(pool);return 1; }// 线程的取消函数 void pthread_cancel_handler(void *arg) {printf("[%ld] is cancel\n", pthread_self());pthread_mutex_unlock((pthread_mutex_t*)arg); // 解锁 }// 线程的例程函数 void *routine(void *arg) {task *task_p; // 任务指针,用来执行将要执行的任务thread_pool *pool = (thread_pool*)arg;while(1){pthread_cleanup_push(pthread_cancel_handler, (void*)&pool->task_list_lock);// 尝试持有互斥锁pthread_mutex_lock(&pool->task_list_lock);// 判断是否有任务,没有则进入睡眠// 1、没有任务,线程池销毁开关断开,则进入条件变量等待队列while(!pool->waiting_tasks && !pool->shutdown){// 当条件不满足时,会先自动解锁pool->lock,然后等待到条件满足后,会自动上锁pool->lockpthread_cond_wait(&pool->task_list_cond, &pool->task_list_lock);}// 2、没有任务,线程池销毁开发闭合,则先解锁,然后退出if(!pool->waiting_tasks && pool->shutdown){pthread_mutex_unlock(&pool->task_list_lock); // 需要解锁pthread_exit(NULL);}// 3、有任务,线程池销毁开关断开,则取一个任务task_p = pool->task_list->next;pool->task_list->next = task_p->next; // 弹出第一任务节点 pool->waiting_tasks--; // 当前等待的任务数量-1,pthread_mutex_unlock(&pool->task_list_lock); // 解锁// 弹出压入的线程取消函数,运行到这里不执行,但是当线程在前面被意外取消或中断会执行pthread_cleanup_pop(0); // 为了防止死锁,执行任务期间不接受线程取消请求pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);(task_p->do_task)(task_p->arg); // 通过函数指针的方式执行任务// 执行完任务后,接受线程取消请求pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);free(task_p); // 删除任务节点}pthread_exit(NULL); }
main.c(线程池操作演示文件)
// 线程池的测试案例#include <stdio.h> #include <unistd.h> #include "thread_pool.h"void *mytask(void *arg) { int n = rand()%10;printf("[%ld][%s] ==> job will be done in %d sec...\n", pthread_self(), __FUNCTION__, n);sleep(n);printf("[%ld][%s] ==> job done!\n", pthread_self(), __FUNCTION__); }void *func(void *arg ) {printf("this is a test by [%s]\n", (char*)arg);sleep(1);printf("test finish...\n"); }void *count_time(void *arg) {int i = 0;while(1){sleep(1);printf("sec: %d\n", ++i);} }int main(void) {pthread_t a;pthread_create(&a, NULL, count_time, NULL);// 1、初始化线程池thread_pool *pool = malloc(sizeof(thread_pool));init_pool(pool, 2);// 2、投放任务printf("throwing 3 tasks...\n");add_task(pool, mytask, NULL);add_task(pool, mytask, NULL);add_task(pool, mytask, NULL);// 3、查看当前线程池中的线程数量printf("current thread number: %d\n", total_thread(pool));sleep(9);// 4、再次投放任务printf("throwing another 2 tasks...\n");add_task(pool, mytask, NULL);add_task(pool, mytask, NULL);add_task(pool, func, (void *)"Great Macro");// 5、添加2条线程printf("try add 2 threads, actual add %d\n", add_thread(pool, 2));printf("current thread number: %d\n", total_thread(pool));sleep(5);// 6、删除3条线程printf("try remove 3 threads, actual remove: %d\n", remove_thread(pool, 3));printf("current thread number: %d\n", total_thread(pool));sleep(5);// 7、 销毁线程池printf("destroy thread pool\n");destroy_pool(pool);return 0; }
注:编译时,把线程池文件和测试文件放在同一个工作目录下。
实际使用时,只需要把thread_pool.h和thread_pool.c拷贝到自己的工程目录下,然后根据规则操作线程池。
六、总结
线程池是许多线程的集合,它不是线程组。线程池适用于任何需要处理大量任务或需要同时处理多个请求的应用程序的场景,可以提供性能和效率。
至此,Linux系统编程系列,16篇完结撒花,历时5天,这年中秋国庆没有假放!!!
相关文章:
Linux系统编程系列之线程池
Linux系统编程系列(16篇管饱,吃货都投降了!) 1、Linux系统编程系列之进程基础 2、Linux系统编程系列之进程间通信(IPC)-信号 3、Linux系统编程系列之进程间通信(IPC)-管道 4、Linux系统编程系列之进程间通信-IPC对象 5、Linux系统…...
Linux CentOS7 vim多文件与多窗口操作
窗口是可视化的分割区域。Windows中窗口的概念与linux中基本相同。连接xshell就是在Windows中新建一个窗口。而vim打开一个文件默认创建一个窗口。同时,Vim打开一个文件也就会建立一个缓冲区,打开多个文件就会创建多个缓冲区。 本文讨论vim中打开多个文…...
SPI 通信协议
1. SPI通信 1. 什么是SPI通信协议 2. SPI的通信过程 在一开始会先把发送缓冲器的数据(8位)。一次性放到移位寄存器里。 移位寄存器会一位一位发送出去。但是要先放到锁存器里。然后从机来读取。从机的过程也一样。当移位寄存器的数据全部发送完。其实…...
【图像处理】使用各向异性滤波器和分割图像处理从MRI图像检测脑肿瘤(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
5个适合初学者的初级网络安全工作,网络安全就业必看
前言 网络安全涉及保护计算机系统、网络和数据免受未经授权的访问、破坏和盗窃 - 防止数字活动和数据访问的中断 - 同时也保护用户的资产和隐私。鉴于公共事业、医疗保健、金融以及联邦政府等行业的网络犯罪攻击不断升级,对网络专业人员的需求很高,这并…...
Kafka核心原理
1、Topic的分片和副本机制 分片作用: 解决单台节点容量有限的问题,节点多,效率提升,吞吐量提升。通过分片,将一个大的容器分解为多个小的容器,分布在不同的节点上,从而实现分布式存储。 分片…...
探秘前后端开发世界:猫头虎带你穿梭编程的繁忙街区,解锁全栈之路
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
洛谷_分支循环
p2433 问题 5 甲列火车长 260 米,每秒行 12 米;乙列火车长220 米,每秒行 20 米,两车相向而行,从两车车头相遇时开始计时,多长时间后两车车尾相离?已知答案是整数。 计算方式:两车车…...
MySQL数据库入门到精通——进阶篇(3)
黑马程序员 MySQL数据库入门到精通——进阶篇(3) 1. 锁1.1 锁-介绍1.2 锁-全局锁1.3 锁-表级锁1.3.1 表级锁-表锁1.3.2 表级锁元数据锁( meta data lock,MDL)1.3.3 表级锁-意向锁1.3.4 表级锁意向锁测试 1.4 锁-行级锁1.4.1 行级锁-行锁1.4.2…...
Mind Map:大语言模型中的知识图谱提示激发思维图10.1+10.2
知识图谱提示激发思维图 摘要介绍相关工作方法第一步:证据图挖掘第二步:证据图聚合第三步:LLM Mind Map推理 实验实验设置医学问答长对话问题使用KG的部分知识生成深入分析 总结 摘要 LLM通常在吸收新知识的能力、generation of hallucinati…...
[引擎开发] 杂谈ue4中的Vulkan
接触Vulkan大概也有大半年,概述一下自己这段时间了解到的东西。本文实际上是杂谈性质而非综述性质,带有严重的主观认知,因此并没有那么严谨。 使用Vulkan会带来什么呢?简单来说就是对底层更好的控制。这意味着我们能够有更多的手段…...
docker--redis容器部署及地理空间API的使用示例-II
文章目录 Redis 地理位置类型API命令操作示例JAVA使用示例导入依赖RedisTemplate 操作GeoData示例CityInfo实体类Geo操作接口类Geo操作接口实现类SpringBoot测试类RedissonClient 操作GeoData示例docker–redis容器部署及与SpringBoot整合 docker–redis容器部署及地理空间API的…...
Vue中如何进行文件浏览与文件管理
Vue中的文件浏览与文件管理 文件浏览与文件管理是许多Web应用程序中常见的功能之一。在Vue.js中,您可以轻松地实现文件浏览和管理功能,使您的应用程序更具交互性和可用性。本文将向您展示如何使用Vue.js构建文件浏览器和文件管理功能,以及如…...
jenkins利用插件Active Choices Plug-in达到联动显示或隐藏参数,且参数值可修改
1. 添加组件 Active Choices Plug-in 如jenkins无法联网,可在以下两个地址中下载插件,然后放到/home/jenkins/.jenkins/plugin下面重启jenkins即可 Active Choices Active Choices | Jenkins plugin 2. 效果如下: sharding为空时…...
香蕉叶病害数据集
1.数据集 第一个文件夹为数据增强(旋转平移裁剪等操作)后的数据集 第二个文件夹为原始数据集 2.原始数据集 Cordana文件夹(162张照片) healthy文件夹(129张) Pestalotiopsis文件夹(173张照片&…...
天地无用 - 修改朋友圈的定位: 高德地图 + 爱思助手
1,电脑上打开高德地图网页版 高德地图 (amap.com) 2,网页最下一栏,点击“开放平台” 高德开放平台 | 高德地图API (amap.com) 3,在新网页中,需要登录高德账户才能操作。 可以使用手机号和验证码登录。 4,…...
AtCoder Beginner Contest 232(A-G)
A - QQ solver (atcoder.jp)直接按题意模拟即可。 B - Caesar Cipher (atcoder.jp)按题意模拟即可 C - Graph Isomorphism (atcoder.jp)按题意模拟即可 D - Weak Takahashi (atcoder.jp) 一个非常套路的网格dp E - Rook Path (atcoder.jp) (1)题意 有…...
计算机网络(第8版)-第5章 运输层
5.1 运输层协议概述 5.1.1 进程之间的通信 图5-1 中两个运输层之间有一个深色双向粗箭头,写明“运输层提供应用进程间的逻辑通信”。 图5-1 运输层为相互通信的应用进程提供了逻辑通信 5.1.2 运输层的两个主要协议 5.1.3 运输层的端口 请注意,这种…...
AtCoder Beginner Contest 231(D-F,H)
D - Neighbors (atcoder.jp) (1)题意 给出M组关系,问是否有一个排列,能表示A[i]和B[i]相邻 (2)思路 考虑如果有环,显然不能满足排列,因为排列中度数最多为2,若有超过2的显…...
【Python】map
map()函数是Python内置函数之一,它的主要作用是将一个函数应用于可迭代对象中的每个元素,并返回一个包含结果的迭代器。 map()函数的语法如下: map(function, iterable)function参数是一个函数,表示要应用于可迭代对象每个元素的…...
Swift 5.9 与 SwiftUI 5.0 中新 Observation 框架应用之深入浅出
0. 概览 Swift 5.9 一声炮响为我们带来全新的宏(Macro)机制,也同时带来了干霄凌云的 Observation 框架。 Observation 框架可以增强通用场景下的使用,也可以搭配 SwiftUI 5.0 而获得双剑合璧的更强威力。 在本篇博文,…...
【已解决】在 Vite 项目中使用 eslint-config-ali 时遇到的解析错误
错误还原 搭建 Vite 项目 pnpm create vite my-vue-app --template vue-ts安装 eslint-config-ali pnpm i -D eslint-config-ali typescript-eslint/parser typescript-eslint/eslint-plugin eslint-plugin-import eslint-import-resolver-typescript vue-eslint-parser esl…...
蓝桥杯每日一题2023.10.5
3420. 括号序列 - AcWing题库 题目描述 题目分析 对于这一我们需要有前缀知识完全背包 完全背包的朴素写法: #include<bits/stdc.h> using namespace std; const int N 1010; int n, m, v[N], w[N], f[N][N]; int main() {cin >> n >> m;fo…...
PyTorch实例:简单线性回归的训练和反向传播解析
文章目录 🥦引言🥦什么是反向传播?🥦反向传播的实现(代码)🥦反向传播在深度学习中的应用🥦链式求导法则🥦总结 🥦引言 在神经网络中,反向传播算法…...
Arcgis提取玉米种植地分布,并以此为掩膜提取遥感影像
Arcgis提取玉米种植地分布上,并以此为掩膜提取遥感影像 一、问题描述 因为之前反演是整个研究区,然而土地利用类型有很多类,只在农田或者植被上进行反演,需要去除水体、建筑等其他类型,如何处理得到下图中只有耕地类…...
软件工程与计算总结(四)项目管理基础
目录 一.项目和项目管理 二.团队组织与管理 三.软件质量保障 四.软件配置管理 五.项目实践 一.项目和项目管理 1.软件开发远不是纯粹的编程,随着软件规模的增长,软件开发活动也变得越来越复杂~ 2.软件项目就是要将所有的软件开发活动组织起来&#…...
【Python】datetime 库
# timedelta(days, seconds, microseconds,milliseconds, minutes, hours, weeks) 默认按顺序传递参数 # 主要介绍 datetime.datetime 类 # 引入 from datetime import datetime today datetime.now() # 获取当前时间 2023-10-05 15:58:03.218651 today1 datetime.utcnow() #…...
从0开始python学习-28.selenium 需要图片验证的登录
url https://test.com/login driver.get(url) # 获取登录页面需要输入账号密码进行模拟登录操作 user driver.find_element(By.XPATH,//*[id"login"]/div[2]/div/form[2]/div[2]/div/div/input).send_keys(username) pwd driver.find_element(By.XPATH,//*[id&qu…...
Nginx搭建Rtmp流媒体服务,并使用Ffmpeg推流
文章目录 1.rtmp流媒体服务框架图2.nginx配置3.配置nginx4.使用ffmpeg推流5.实时推摄像头流 本项目在开发板上使用nginx搭建流媒体服务,利用ffmpeg进行推流,在pc上使用vlc media进行拉流播放。 1.rtmp流媒体服务框架图 2.nginx配置 下载:wge…...
IDEA 将一个普通Java工程转化为maven工程
打开IntelliJ IDEA并打开Java工程。 在项目窗口中,右键单击项目名称,选择“Add Framework Support”。 在弹出的窗口中,选择“Maven”。 在“Maven Information”窗口中,填写Group Id、Artifact Id和Version等基本信息。 点击…...
购物网站建设和使用/网站关键词优化价格
计算机系统大作业题 目 程序人生-Hello’s P2P 专 业 软件工程 学 号 1183710110 班 级 1837101 学 生 孙英玮 指 导 教 师 史先俊 摘要本文主要介绍了hello.c在Linux下生命周期,借助了Linux下一系列开发工具,通过对其预处理、编译、汇编、链接等过…...
网站备案技巧/seo优缺点
自动化测试https://www.alltesting.cn/jsp/newVersion2/bigNews/alltestingAR.jsp 自动化测试框架AutoTestFramework( 简称:ATF)是可以提供自动化测试执行和管理功能的架构的综合自动化测试框架,可以集成基础自动化测试工具,用于组织、管理和执行那些独…...
建站大师排名表2021/广东公司搜索seo哪家强
说明 适合 vue2.0版本的官方链接如下:Vue I18nVue I18n 是 Vue.js 的国际化插件https://kazupon.github.io/vue-i18n/zh/ 适合vue3.0 版本的官方链接如下(目前该文档只有英文和日语,中文翻译需要借助浏览器-2021-12-11 08:53&a…...
论坛类网站建站/淘宝seo排名优化软件
原创作者:HackLiu 000 前言 如果你家里有多台设备需要联网需要娱乐,你一定会或多或少遇到设备碎片化带来的烦恼。当然,已经有很多厂商包括新晋的小米、360在内的互联网公司做了这个事情,给你搞个家庭存储中心,基…...
scala做网站/百度西安
public class Book {public static final String url "asfdafs"; }获取这个属性Field field Book.class.getDeclaredFields()[2];输出Log.i(TAG, field.get(null) "");如果想获取你在编写的时候就赋予的值,必须把它弄成静态,只有…...
住房和城乡建设局网站/杭州关键词自动排名
路由 hash模式(网址上带#号)hlstory模式(网址上没有#号,正式上线需要服务端支持) 准备登陆组件并添加路由 在 src/views 新建一个 Login/indexvue 页面,用作陆页面在router 文件里去写路由规则在app.vue …...