[线程/C]基础
文章目录
- 1. 线程介绍
- 2. 创建线程
- 2.1 线程函数
- 2.2 创建线程
- 3. 线程退出
- 4. 线程回收
- 4.1 线程函数
- 4.2 回收子线程数据
- 4.2.1 使用子线程栈
- 4.2.2 使用全局变量
- 4.2.3 使用主线程栈
- 5. 线程分离
- 6. 其他线程函数
- 6.1 线程取消
- 6.2 线程ID的比较
1. 线程介绍
线程是轻量级的进程(LWP:light weight process),在Linux环境下线程的本质仍是进程。
在计算机上运行的程序是一组指令及指令参数的组合,指令按照既定的逻辑控制计算机运行。
操作系统会以进程为单位,分配系统资源,可以这样理解
进程是资源分配的最小单位,线程是操作系统调度执行的最小单位。
从概念上来说线程和进程的区别:
-
进程有自己独立的地址空间, 多个线程共用同一个地址空间
- 线程更节省系统资源, 效率不仅可以保持, 而且能更高
- 在一个地址空间中多个线程独享: 每个线程都有属于自己的栈区, 寄存器(内核中管理的)
- 在一个地址空间中多个线程共享: 代码段, 堆区, 全局数据区, 打开的文件(文件描述符表)
-
线程是程序的最小执行单位, 进程是操作系统中最小的资源分配单位
- 每个进程对应一个虚拟地址空间,一个进程只能抢一个CPU时间片
- 一个地址空间中可以划分出多个线程, 能在有效的资源基础上, 抢更多的CPU时间片
- CPU的调度和切换: 线程的上下文切换比进程要快的多
上下文切换:进程/线程分时复用CPU时间片,在切换之前会将上一个任务的状态进行保存, 下次切换回这个任务的时候, 加载这个状态继续运行,任务从保存到再次加载
这个过程就是一次上下文切换。 - 线程更加廉价, 启动速度更快, 退出也快, 对系统资源的冲击小。
在处理多任务程序的时候使用多线程比使用多进程要更有优势,但是线程并不是越多越好
- 文件IO操作:文件IO对CPU是使用率不高, 因此可以分时复用CPU时间片
线程的个数 = 2 * CPU核心数 (效率最高) - 处理复杂的算法(主要是CPU进行运算, 压力大)
线程的个数 = CPU的核心数 (效率最高)
2. 创建线程
2.1 线程函数
每一个线程都有一个唯一的线程ID,ID类型为
pthread_t
这个ID是一个无符号长整形(unsigned long
)
如果想要得到当前线程的线程ID,可以调用如下函数:
pthread_t pthread_self(void); // 返回当前线程的线程ID
在一个进程中调用线程创建函数,就可得到一个子线程
和进程不同,需要给每一个创建出的线程指定一个处理函数,否则这个线程无法工作。
#include <pthread.h>
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
// Compile and link with -pthread
//线程库的名字叫pthread, 全名: libpthread.so libptread.a
介绍一下pthread_create函数的
- 参数
thread
: 传出参数,无符号长整形,线程创建成功, 会将线程ID写入到这个指针指向的内存中
attr
: 线程的属性, 一般情况下使用默认属性即可, 写NULL
start_routine
: 函数指针,创建出的子线程的处理动作,该函数在子线程中执行。
arg
: 作为实参传递到 start_routine 指针指向的函数内部 - 返回值:线程创建成功返回0,创建失败返回对应的错误号
2.2 创建线程
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h> 子线程的处理代码
void* Work(void* args)
{printf("子线程id: %ld\n",pthread_self());for(int i = 0; i < 5 ; ++i){printf("child == i : %d\n",i);}return NULL;
}
int main()
{//1.创建子线程pthread_t tid;pthread_create(&tid,NULL,Work,NULL);printf("子线程创建成功,线程id : %ld\n",tid);//2.子线程不会执行下面的代码,由主线程执行printf("主线程id : %ld\n", pthread_self());for(int i = 0; i < 3 ; ++i){printf("i == %d\n",i);}//进行休息,否则可能导致子线程未执行时,主线程已经结束,直接结束了进程sleep(1);return 0;
}
使用gcc编译时容易出现的错误
gcc pthread_create.c
错误原因是编译器链接不到线程库文件(动态库),需在编译时通过参数指定出来
动态库名为 libpthread.so
需要使用的参数为 -l
据规则掐头去尾最终形态应该写成:-lpthread
(参数和参数值中间可以有空格)。
所以正确的写法是
gcc pthread_create.c -lpthread
在打印的输出中为什么子线程处理函数没有执行呢(只看到了子线程的部分日志输出?
主线程一直在运行, 执行期间创建出了子线程,说明主线程有CPU时间片, 在这个时间片内将代码执行完毕了, 主线程就退出了。
子线程被创建出来之后需要抢cpu时间片, 抢不到就不能运行,如果主线程退出了,虚拟地址空间就被释放了, 子线程一并销毁
。
但是如果某一个子线程退出了, 主线程仍在运行, 虚拟地址空间依旧存在。
结论:在没有人为干预的情况下,虚拟地址空间的生命周期和主线程是一样的,与子线程无关。
目前的解决方案: 让子线程执行完毕, 主线程再退出, 可以在主线程中添加挂起函数sleep();
3. 线程退出
在编写多线程程序的时候,如果想要让线程退出,但不导致虚拟地址空间的释放(针对于主线程)
我们就可以调用线程库中的线程退出函数,只要调用该函数当前线程就马上退出了,并且不会影响到其他线程的正常运行,不管是在子线程或者主线程中都可以使用
。
#include <pthread.h>
void pthread_exit(void *retval);
- 参数:
线程退出时携带的数据,当前子线程的主线程会得到该数据。如果不需要使用,指定为NULL
eg:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 子线程的处理代码
void* working(void* arg)
{sleep(1);printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){if(i==6){pthread_exit(NULL); // 直接退出子线程} printf("child == i: = %d\n", i);}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 主线程调用退出函数退出, 地址空间不会被释放pthread_exit(NULL);return 0;
}
4. 线程回收
4.1 线程函数
线程和进程一样,子线程退出的时候其内核资源主要由主线程回收
线程库中提供的线程回收函叫做pthread_join()
这个函数是一个阻塞函数
子线程在运行, 调用该函数就会阻塞
子线程退出, 函数解除阻塞进行资源的回收.
函数被调用一次,只能回收一个子线程,如果有多个子线程则需要循环进行回收。
另外通过线程回收函数还可以获取到子线程退出时传递出来的数据,函数原型如下:
#include <pthread.h>
// 这是一个阻塞函数, 子线程在运行这个函数就阻塞
// 子线程退出, 函数解除阻塞, 回收对应的子线程资源, 类似于回收进程使用的函数 wait()
int pthread_join(pthread_t thread, void **retval);
- 参数:
- thread: 要被回收的子线程的线程ID
- retval: 二级指针, 指向一级指针的地址, 是一个传出参数, 这个地址中存储了pthread_exit() 传递出的数据,如果不需要这个参数,可以指定为NULL
- 返回值:线程回收成功返回0,回收失败返回错误号。
4.2 回收子线程数据
在子线程退出的时候可以使用
pthread_exit()
的参数将数据传出
在回收这个子线程的时候可以通过phread_join()
的第二个参数来接收子线程传递出的数据。接收数据有很多种处理方式,列举几种:
4.2.1 使用子线程栈
通过函数
pthread_exit(void *retval);
可以得知,子线程退出的时候,需要将数据记录到一块内存中,通过参数传出的是存储数据的内存的地址,而不是具体数据,因为参数是void*类型,所有这个万能指针可以指向任意类型的内存地址。先来看第一种方式,将子线程退出数据保存在子线程自己的栈区:
// pthread_join.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 定义结构
struct Persion
{int id;char name[36];int age;
};// 子线程的处理代码
void* working(void* arg)
{printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf("child == i: = %d\n", i);if(i == 6){struct Persion p;p.age =12;strcpy(p.name, "tom");p.id = 100;// 该函数的参数将这个地址传递给了主线程的pthread_join()pthread_exit(&p);}}return NULL; // 代码执行不到这个位置就退出了
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 阻塞等待子线程退出void* ptr = NULL;// ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存// 这个内存地址就是pthread_exit() 参数指向的内存pthread_join(tid, &ptr);// 打印信息struct Persion* pp = (struct Persion*)ptr;printf("子线程返回数据: name: %s, age: %d, id: %d\n", pp->name, pp->age, pp->id);printf("子线程资源被成功回收...\n");return 0;
}
但是当我们编译时打印出的信息并没有得到子线程的数据
具体原因是这样的:
如果多个线程共用同一个虚拟地址空间,每个线程在栈区都有一块属于自己的内存,相当于栈区被这几个线程平分了,当线程退出,线程在栈区的内存也就被回收了,因此随着子线程的退出,写入到栈区的数据也就被释放了。
4.2.2 使用全局变量
位于同一虚拟地址空间中的线程,虽然不能共享栈区数据,但可以共享全局
数据区和堆区
数据,因此在子线程退出的时候可以将传出数据存储到全局变量、静态变量或者堆内存中。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 定义结构
struct Persion
{int id;char name[36];int age;
};struct Persion p; // 定义全局变量// 子线程的处理代码
void* working(void* arg)
{printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf("child == i: = %d\n", i);if(i == 6){// 使用全局变量p.age =12;strcpy(p.name, "tom");p.id = 100;// 该函数的参数将这个地址传递给了主线程的pthread_join()pthread_exit(&p);}}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 阻塞等待子线程退出void* ptr = NULL;// ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存// 这个内存地址就是pthread_exit() 参数指向的内存pthread_join(tid, &ptr);// 打印信息struct Persion* pp = (struct Persion*)ptr;printf("name: %s, age: %d, id: %d\n", pp->name, pp->age, pp->id);printf("子线程资源被成功回收...\n");return 0;
}
4.2.3 使用主线程栈
虽然每个线程都有属于自己的栈区空间,但是位于
同一个地址空间的多个线程是可以相互访问对方的栈空间上的数据的。
由于很多情况下还需要在主线程中回收子线程资源,所以主线程一般都是最后退出,基于这个原因在下面程序中将子线程返回的数据保存到了主线程的栈区内存中:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 定义结构
struct Persion
{int id;char name[36];int age;
};// 子线程的处理代码
void* working(void* arg)
{struct Persion* p = (struct Persion*)arg;printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf("child == i: = %d\n", i);if(i == 6){// 使用主线程的栈内存p->age =12;strcpy(p->name, "tom");p->id = 100;// 该函数的参数将这个地址传递给了主线程的pthread_join()pthread_exit(p); //p本来就是指针}}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;struct Persion p;// 主线程的栈内存传递给子线程pthread_create(&tid, NULL, working, &p);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 阻塞等待子线程退出void* ptr = NULL;// ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存// 这个内存地址就是pthread_exit() 参数指向的内存pthread_join(tid, &ptr);// 打印信息printf("name: %s, age: %d, id: %d\n", p.name, p.age, p.id);printf("子线程资源被成功回收...\n");return 0;
}
在上面的程序中,调用pthread_create()创建子线程,并将主线程中栈空间变量p的地址传递到了子线程中,在子线程中将要传递出的数据写入到了这块内存中。
也就是说在程序的main()函数中,通过指针变量ptr或者通过结构体变量p都可以读出子线程传出的数据。
5. 线程分离
在某些情况下,程序中的主线程有属于自己的业务处理流程,如果让主线程负责子线程的资源回收,调用pthread_join()只要子线程不退出主线程就会一直被阻塞,主要线程的任务也就不能被执行了。
在线程库函数中为我们提供了线程分离函数pthread_detach()
,调用这个函数之后指定的子线程就可以和主线程分离,当子线程退出的时候,其占用的内核资源就被系统的其他进程接管并回收了。线程分离之后在主线程中使用pthread_join()就回收不到子线程资源了。
#include <pthread.h>
// 参数是子线程的线程ID, 主线程就可以和这个子线程分离了
int pthread_detach(pthread_t thread);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 子线程的处理代码
void* working(void* arg)
{printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf("child == i: = %d\n", i);}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 设置子线程和主线程分离pthread_detach(tid);// 让主线程自己退出即可pthread_exit(NULL);return 0;
}
6. 其他线程函数
6.1 线程取消
线程取消的意思就是在某些特定情况下在一个线程中杀死另一个线程。
使用这个函数杀死一个线程需要分两步:
在线程A中调用线程取消函数pthread_cancel,指定杀死线程B,这时候线程B是死不了的
在线程B中进程一次系统调用
(从用户区切换到内核区),否则线程B可以一直运行。
#include <pthread.h>
int pthread_cancel(pthread_t thread);
- 参数:要杀死的线程的线程ID
- 返回值:函数调用成功返回0,调用失败返回非0错误号。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 子线程的处理代码
void* working(void* arg)
{int j=0;for(int i=0; i<9; ++i){j++;}// printf函数会调用系统函数, 因此这是个间接的系统调用printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf(" child i: %d\n", i);}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 杀死子线程, 如果子线程中做系统调用, 子线程就结束了pthread_cancel(tid);// 让主线程自己退出即可pthread_exit(NULL);return 0;
}
关于系统调用有两种方式:
- 直接调用Linux系统函数
- 调用标准C库函数,为了实现某些功能,在Linux平台下标准C库函数会调用相关的系统函数
6.2 线程ID的比较
在Linux中线程ID本质就是一个无符号长整形,因此可以直接使用比较操作符比较两个线程的ID,但是线程库是可以跨平台使用的,在某些平台上 pthread_t可能不是一个单纯的整形,这中情况下比较两个线程的ID必须要使用比较函数
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
- 参数:t1 和 t2 是要比较的线程的线程ID
- 返回值:如果两个线程ID相等返回非0值,如果不相等返回0
相关文章:
[线程/C]基础
文章目录 1. 线程介绍2. 创建线程2.1 线程函数2.2 创建线程 3. 线程退出4. 线程回收4.1 线程函数4.2 回收子线程数据4.2.1 使用子线程栈4.2.2 使用全局变量4.2.3 使用主线程栈 5. 线程分离6. 其他线程函数6.1 线程取消6.2 线程ID的比较 1. 线程介绍 线程是轻量级的进程&#x…...
Spring Clould 负载均衡 - Ribbon
视频地址:微服务(SpringCloudRabbitMQDockerRedis搜索分布式) Ribbon-负载均衡原理(P14) 具体实现时通过LoaBalanced注解实现,表示RestTemplate要被Ribbon拦截处理 orderservice调用user时候,…...
活用DNS技术实现相同IP的不同端口映射不同域名
WindowsDNS基本配置 在内网的 Windows 服务器环境中,你可以通过配置 DNS 服务和 Web 服务器来实现所需的域名解析和端口转发。如下是一些基本的步骤来实现配置: 1,配置 Windows DNS 服务 在你的 Windows 服务器上配置 DNS 服务,…...
AutoHotkey:定时删除目录下指定分钟以前的文件,带UI界面
删除指定目录下,所有在某个指定分钟以前的文件,可以用来清理经常生成很多文件的目录,但又需要保留最新的一部分文件 支持拖放目录到界面 能够记忆设置,下次启动后不用重新设置,可以直接开始 应用场景比如:…...
一文学会sklearn中的交叉验证的方法
前言 在机器学习中,我们经常需要评估模型的性能。而为了准确评估模型的性能,我们需要使用一种有效的评估方法。五折交叉验证(5-fold cross-validation)就是其中一种常用的模型评估方法,用于评估机器学习模型的性能和泛…...
【MySQL面试题(66道)】
文章目录 MySQL面试题(66道)基础1.什么是内连接、外连接、交叉连接、笛卡尔积呢?2.那 MySQL 的内连接、左连接、右连接有有什么区别?3.说一下数据库的三大范式?4.varchar 与 char 的区别?5.blob 和 text 有什么区别?6.…...
CSSCI、北核期刊投稿指南(2023年更新)
该数据为经管类的期刊投稿指南,包含发表难度,文章数量,影响因子,用户评价等指标。共5份文件,分别为国内所有期刊信息库、投稿指南(CSSCI版本、CSSCI扩展版本、北大核刊版本、建议期刊版本) 一、…...
构建 NodeJS 影院微服务并使用 docker 部署它(02/4)
一、说明 构建一个微服务的电影网站,需要Docker、NodeJS、MongoDB,这样的案例您见过吗?如果对此有兴趣,您就继续往下看吧。 图片取自网络 — 封面由我制作 这是✌️“构建 NodeJS 影院微服务”系列的第二篇文章。 二、对第一部分的…...
HTML <style> 标签
实例 <html> <head> <style type="text/css"> h1 {color:red} p {color:blue} </style> </head><body> <h1>Header 1</h1> <p>A paragraph.</p> </body> </html>定义和用法 <style>…...
设计模式——迪米特法则
文章目录 基本介绍应用实例应用实例改进迪米特法则注意事项和细节 基本介绍 一个对象应该对其他对象保持最少的了解类与类关系越密切,耦合度越大迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说&#x…...
区块链基本概念与当前生态简介
区块链是一种去中心化的分布式账本技术,它通过将数据按照时间顺序链接成区块,并使用密码学算法确保数据的安全性和完整性。每个区块包含一定数量的交易记录,而且每个区块都包含了前一个区块的哈希值,这样形成了一个不可篡改的链式…...
mac安装lrzsz出错Command failed with exit 128: git
终端检查电脑是否安装了rz和sz which sz若报错,则需要下载。由于网络和代理的原因,以下命令会报错: brew install lrzsz是因为brew和git配置的代理存在冲突,对于无外网链接功能,无特殊配置的git而言,需要…...
“深入探索JVM内部机制:揭秘Java虚拟机“
标题:深入探索JVM内部机制:揭秘Java虚拟机 摘要:本文将深入探索Java虚拟机(JVM)的内部机制,从内存管理、垃圾回收、即时编译等方面进行详细剖析。通过了解JVM的工作原理,我们可以更好地理解Jav…...
lvs-DR
lvs-DR数据包流向分析 client向目标VIP发出请求。 DIR根据负载均衡算法一台active的RS(RIR1),将RIP1所在的网卡的mac地址作为目标的mac地址,发送到局域网里。 RIRI在局域网中的收到这个帧,拆开后发现目标(…...
Vue 项目运行 npm install 时,卡在 sill idealTree buildDeps 没有反应
解决方法:切换到淘宝镜像。 以下是之前安装的 xmzs 包,用于控制切换淘宝镜像。 该截图是之前其他项目切换淘宝镜像的截图。 切换镜像后,顺利执行 npm install 。...
ShardingSphere介绍
ShardingSphere从4.X到5.X的内容发生了很多的改变,感兴趣的伙伴可以到ShardingSphere的博客查看各个版本的新特性。https://blog.csdn.net/ShardingSphere?typeblog 此次使用最新版本 shardingShpere5.4.0,实现数据库读写分离、数据分片、分布式事务等…...
【SA8295P 源码分析】44 - 如何替换 NON-HLOS.bin 中的 Wifi Firmware 固件
【SA8295P 源码分析】44 - 如何替换 NON-HLOS.bin 中的 Wifi Firmware 固件 1、提取 NON-HLOS.bin 中的 Wifi Firmware 出来2、把提取出来的 wifi 固件放到代码中3、重新打包生成 NON-HLOS.bin4、将生成的 NON-HLOS.bin 与 老的 NON-HLOS.bin 对比5、使用fastboot 下载测试wifi…...
市面上那里有稳定L2股票行情数据接口?
随着市场的发展和技术的进步,level2股票行情数据接口已经成为股票交易软件的标准配置之一。虽然这些券商软件的功能在很大程度上相似,但它们仍然有自己的特点和优势。 例如:通过股票交易所以其专业的研究报告和丰富的信息服务而受到广泛关注&…...
个人信息保护影响评估(PIA)怎么做?解发条件、实施步骤、操作指南
个人信息保护一直是人们关注的热点话题,互联网、人工智能、大数据等新兴技术的快速发展极大地增强了入侵个人信息的能力,对个人信息的随意收集、违法获取、过度使用、非法买卖、泄露等问题引起了全球各国的普遍关注。同时随着用户的个人信息保护意识的逐…...
HTML <sub> 标签
例子 这段文本包含 <sub>下标</sub> 定义和用法 <sub> 标签可定义下标文本。 包含在 <sub> 标签和其结束标签 </sub> 中的内容将会以当前文本流中字符高度的一半来显示,但是与当前文本流中文字的字体和字号都是一样的。 提示&am…...
C# 设置、获取程序,产品版本号
右键,程序属性。打开“程序集信息” 选择需要设置的版本信息。下面的代码,获取不同的设置内容。 string 其他 Assembly.GetExecutingAssembly().FullName; string 程序集版本 Assembly.GetExecutingAssembly().G…...
LeetCode 面试题 01.04. 回文排列
文章目录 一、题目二、C# 题解 一、题目 给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一。 回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。 回文串不一定是字典当中的单词。 点击此处跳转题目。 示例1: 输入&…...
CentOS 7 安装MySQL8.0.33
一、查看 CentOS 版本 要查看当前 CentOS 版本,你可以执行以下命令: cat /etc/centos-release 该命令将显示当前 CentOS 的版本信息,例如: CentOS Linux release 7.9.2009 (Core) 在这个示例中,CentOS 版本为 7.…...
OpenCV(二)——图像基本处理(四)
目录 4.图像形态学操作 4.1 图像腐蚀 4.2 图像膨胀 4.3 开运算 4.4 闭运算...
11.小程序的配置项
window导航配置 全局配置通过 app.json进行 “window”: { “backgroundTextStyle”: “light”, “navigationBarBackgroundColor”: “#fff”, “navigationBarTitleText”: “Weixin”, “navigationBarTextStyle”: “black” }, 局部配置通过页面的xx.json配置 { “navig…...
一文科普,配资门户网是什么?
配资门户网是一个为投资者提供配资服务的平台。配资是指通过借用他人资金进行投资交易的一种金融操作方式。配资门户网作为一个线上平台,为投资者提供了方便、快捷的配资服务。 配资门户网提供了多种不同的配资方案,以满足不同投资者的需求。投资者可以…...
编写一个俄罗斯方块
编写俄罗斯方块 思路。 1、创建容器数组,方块, 2、下落,左右移动,旋转,判断结束,消除。 定义一个20行10列的数组表示游戏区。初始这个数组里用0填充,1表示有一个方块,2表示该方块固…...
认识容器,走进Docker
文章目录 容器技术简介容器的核心技术容器平台技术容器的支持技术 Docker理念Docker安装配置阿里云镜像加速器 容器技术简介 一切在云端,万物皆容器,说到容器,大家都会想到Docker,Docker现在几乎是容器的代名词,什么是Docker&…...
初始web
华子目录 前后端与全栈BS架构网页开发原则前端三剑客初始htmlhtml的基本框架如何使用vscode创建网页网页基本框架html基本标签 前后端与全栈 前端:给用户看的内容 – 荧幕前(负责显示) 后端:在后台处理数据 – 荧幕后(负责处理) …...
JVM中释放内存的三种方法
判断是否需要垃圾回收可以采用分析。 1标记--清除算法 分为两个阶段,标记和清除,先利用可达性分型标记还存活的对象,之后将没有被标记的对象删除,这样容易生成空间碎片,而且效率不稳定 标记阶段: 标记阶段…...
wordpress 热门排行/百度seo优化包含哪几项
在spring的配置文件中配置的bean,spring会进行依赖注入和初始化对象。 根据配置不同,spring会选择不同的代理方式。对于JDK动态代理、cglib动态代理,spring会找到目标接口的实现类并初始化一个对象,对于Dubbo的consumerÿ…...
东莞做网站定制/网站排名顾问
import osimport timesource [D:\\MyDrivers\hotfix] #这里可以用自然字符串表示r,因为windows下的分隔符与python的有冲突,所以需要转义字符\# 2. 备份文件到目标路径target_dir F:\\DMDownLoad\\ #这里的末尾一定不要丢分隔符,否者创建…...
做代购需要什么网站/百度获客
开始于2020年6月15日 方法str.title()以首字母大写的方式显示每个单词 name "ada lovelace" print(name.title()) # Ada Lovelace 方法str.upper()和str.lower() name "Ada Lovelace" print(name.upper()) # ADA LOVELACE print(name.lower()) # ada lov…...
手机网站建站用哪个软件好/制作一个网站步骤
众所周知,redis是单线程的。 多个客户端发来的所有指令会按接受到的顺序一个个的执行。 那么multi,exec的作用是什么呢?和pipeline区别在哪? 如果只是简单的认为,multi可以在客户端打包要执行的命令批量的提交到服务端…...
dw动态网站怎么做搜索框/常见的网络营销方式有哪些
只要能存储数据的器件都可以称之为存储器,它的含义覆盖了寄存器,缓存,内存,硬盘。 cpu访问快慢的速度依次为:寄存器-> 缓存->内存->硬盘 寄存器(register)是中央处理器的组成部分,是…...
北海做网站哪家好/投广告的平台有哪些
查看默认iNode的默认大小 转载于:https://www.cnblogs.com/todayORtomorrow/p/10492502.html...