【Linux初阶】多线程1 | 页表的索引作用 线程基础

news2024/11/23 21:03:18

本文要点

  • 再次理解页表,了解页表是如何利用虚拟地址进行索引,实现数据读取和传输的
  • 了解线程概念,线程的优缺点,线程异常的后果
  • 了解线程和进程的差异
  • 了解线程库及其基本调用接口(进程创建、终止、等待、控制),学习线程控制的简单示例
  • 了解C++对多线程的引入

文章目录

  • ☀️一、深入理解页表
  • ☀️二、Linux线程概念
    • 🌻1.什么是线程(重点)
      • ⚡(1)线程的概念
      • ⚡(2)线程库初识
    • 🌻2.线程的优点
    • 🌻3.线程的缺点
    • 🌻4.线程异常(重点)
    • 🌻5.线程用途
  • ☀️三、Linux线程VS进程
    • 🌻1.线程和进程
    • 🌻2.线程共享
  • ☀️四、Linux线程控制
    • 🌻1.POSIX线程库
    • 🌻2.创建线程
    • 🌻3.线程终止
    • 🌻4.线程等待
    • 🌻5.线程控制代码示例
      • ⚡(1)简单版(重点)
      • ⚡(2)复杂版
  • ☀️五、C++多线程引入
  • ☀️结语


☀️一、深入理解页表

在这里插入图片描述

知识点1

  • 地址空间(task_struct)是进程能看到的资源窗口。
  • 页表决定进程真正拥有的资源情况。
  • 合理的对地址空间+页表进行资源划分,我们就可以对一个进程的所有资源进行分类。

知识点2

  • 磁盘的数据储存单元为页帧(4KB),物理内存被划分为一个一个数据页/页框(4KB)进行管理,磁盘可以将数据加载到内存。
  • 虚拟地址需要用32位比特位来表示,设计者们有意的将其拆分为10、10、12的位数进行应用,方便对内存空间的映射。
  • 页表分为页目录页表项
  • 页目录使用虚拟地址的前10位进行索引,指向不同的页表项
  • 页表项使用虚拟地址的中10位进行索引,指向不同物理内存中的页框的起始地址
  • 操作系统依据某一页框的起始地址,以虚拟地址后12位作为偏移量,找到该页框内某一具体物理地址
  • 在读取或写入某个数据时,可直接根据数据大小,在某一具体物理地址直接向下读取即可。
  • 页表根据需要进行创建,不发生映射关系时,对应的数据页表不创建,可大大减少资源占用。

☀️二、Linux线程概念

🌻1.什么是线程(重点)

⚡(1)线程的概念

  • 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
  • 一个进程至少有一个执行线程,可以有多个执行线程。
  • 线程在进程内部运行,本质是在进程地址空间内运行,它拥有进程的一部分资源。
  • 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化。
  • 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。
  • 在Linux下,每个线程共享一个地址空间,它们使用 task_strcut类型的数据结构对虚拟内存进行管理,进而对整个进程资源分配。
  • 在window下,有为线程专门设计的数据结构(TCB)。

在这里插入图片描述

  • Linux内核中有没有真正意义的线程呢?没有,Linux是用进程PCB来模拟线程的,是一套完全属于自己的线程方案。
  • 站在CPU视角,每一个PCB,都可以将其称之为轻量级进程。
  • Linux线程是CPU调度的基本单位,而进程是承担系统资源分配的基本单位。
  • 进程用来申请整体资源,线程用来伸手向进程要资源。
  • Linux没有真正意义上的线程 - Linux无法直接向外部提供线程的系统调用接口,而只能给我们提供创建轻量级进程的接口(pthread库接口)。
  • 好处:简单,维护成本低 - 可靠高效(避免数据结构复杂化)。

⚡(2)线程库初识

  • 我们可以通过调用用户级线程库(pthread库),让库帮我们访问对应的系统调用接口,创建轻量级进程,也就是我们Linux下的线程。
  • 任何一款Linux操作系统都会默认携带这个 pthread库,这种我们将其称之为原生线程库
mythread:mythread.cc
	g++ -o $@ $^ -lpthread -std=c++11  //-lpthread - 使用pthread库
.PHONY:clean
clean:
	rm -f mythread

在这里插入图片描述

  • 多线程程序运行起来之后

在这里插入图片描述

  • LWP:light weight process - 轻量级进程id。
  • PID = LWP,该线程为主线程。
  • PID != LWP,该线程为新线程。
  • CPU调度的时候,以LWP为标识符表示特定执行流。
  • 创建一个线程 -> 创建PCB -> 把对应的代码分配给它(分配一个函数给它)

🌻2.线程的优点

  • 创建一个新线程的代价要比创建一个新进程小得多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源要比进程少很多。
  • 能充分利用多处理器的可并行数量。
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务。
  • 计算密集型应用(加密、解密、算法),为了能在多处理器系统上运行,将计算分解到多个线程中实现。
  • I/O密集型应用(外设、网络拉取),为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

重点:与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。1.进程:切换页表 & 虚拟空间地址 & 切换PCB & 切换上下文。2.线程:切换PCB & 切换上下文。

知识补充:CPU内部会集成一个硬件cache,它负责缓存常用的热点数据,当线程切换时,cache不用被切换,CPU/寄存器 可以直接从该集成硬件中直接读取数据。当进程切换时,cache需要被切换。

在这里插入图片描述

🌻3.线程的缺点

  • 性能损失

一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。

  • 健壮性/鲁棒性 降低(重点)

1.编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。2.一个线程出异常,会影响其他线程,因为信号是发给进程整体的,同一个进程内的不同线程共享同一个PID

  • 缺乏访问控制

进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

  • 编程难度提高。

编写与调试一个多线程程序比单线程程序困难得多。

🌻4.线程异常(重点)

  • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃
  • 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出。
  • 多线程 不等于 多进程(父子进程)子进程崩溃不一定会影响父进程运行

🌻5.线程用途

  • 合理的使用多线程,能提高CPU密集型程序的执行效率。

  • 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是

    多线程运行的一种表现)。


☀️三、Linux线程VS进程

🌻1.线程和进程

  • 进程是资源分配的基本单位
  • 线程是CPU调度的基本单位
  • 线程共享进程数据,但也拥有自己的一部分数据:

线程ID,一组寄存器(上下文),栈,errno,信号屏蔽字,调度优先级

总结:线程一旦被创建,几乎所有资源都是被线程共享的。线程也有自己的私有资源,1.PCB属性私有;2.一定私有的上下文结构;3.每个线程都有自己独立的栈结构。

🌻2.线程共享

进程的多个线程共享同一地址空间,因此Text Segment(代码段)、Data Segment(数据段)都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

  • 文件描述符表
  • 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
  • 当前工作目录
  • 用户id和组id

进程和线程的关系如下图:

在这里插入图片描述


☀️四、Linux线程控制

🌻1.POSIX线程库

  • 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的。
  • 要使用这些函数库,要通过引入头文件<pthread.h>
  • 链接这些线程函数库时要使用编译器命令“-lpthread”选项。
mythread:mythread.cc
	g++ -o $@ $^ -lpthread -std=c++11  //-lpthread - 使用pthread库
.PHONY:clean
clean:
	rm -f mythread

🌻2.创建线程

功能:创建一个新的线程
原型
	int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(*start_routine)(void*), void *arg);
参数
	thread:返回线程ID
	attr:设置线程的属性,attr为NULL表示使用默认属性
	start_routine:是个函数地址,线程启动后要执行的函数
	arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码

错误检查

  • 传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
  • pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做),而是将错误代码通过返回值返回。
  • pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值判定,因为读取返回值要比读取线程内的errno变量的开销更小
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>

void* rout(void* arg) {
	int i;
	for (; ; ) {
		printf("I'am thread 1\n");
		sleep(1);
	}
}
int main(void)
{
	pthread_t tid;
	int ret;
	if ((ret = pthread_create(&tid, NULL, rout, NULL)) != 0) {
		fprintf(stderr, "pthread_create : %s\n", strerror(ret)); //打印创建失败信息
		exit(EXIT_FAILURE);
	}
	int i;
	for (; ; ) {
		printf("I'am main thread\n");
		sleep(1);
	}
}

🌻3.线程终止

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

  1. 从线程函数return。(return nullptr;)这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己。
  3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。
  • pthread_exit函数
功能:线程终止
原型
	void pthread_exit(void* value_ptr);
参数
	value_ptr : value_ptr不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

常见使用方式:
    pthread_exit(nullptr);
  • pthread_cancel函数
功能:取消一个执行中的线程
原型
	int pthread_cancel(pthread_t thread);
参数
	thread:线程ID
返回值:成功返回0;失败返回错误码

🌻4.线程等待

  • 线程也是需要被等待的!如果不等待,就会出现类似僵尸进程的问题 - 内存泄漏。
  • 即已经退出的线程,其空间没有被释放,仍然在进程的地址空间内,创建新的线程不会复用刚才退出线程的地址空间。、
  • 线程等待可以获取线程的退出信息,并回收对应的PCB等内核资源,防止内存泄漏
功能:等待线程结束
原型
	int pthread_join(pthread_t thread, void **value_ptr);
参数
	thread:线程ID
	value_ptr:它指向一个指针,后者指向线程的返回值(void*是一个输出型参数,指向线程的返回值)
返回值:成功返回0;失败返回错误码
常见用法:
    void *ret = nullptr;
	int n = pthread_join(tid, (void**)&ret);
	assert(n == 0);
  • 当我们线程 return (void*)n 的时候,pthread库会为我们维护一个小的变量用于保存这个 (void*)n 变量,我们可以通过 (void**)&ret 的方法将库中的变量拿出来。
  • 我们可以通过下图加深理解,int **pp = &p,*pp=p,*p=a,若a为我们要取出来的值,则(void**)&p两次解引用即可得到a的值

在这里插入图片描述

🌻5.线程控制代码示例

⚡(1)简单版(重点)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

void* thread1(void* arg)
{
	printf("thread 1 returning ... \n");
	int* p = (int*)malloc(sizeof(int));
	*p = 1;
	return (void*)p;
}

void* thread2(void* arg)
{
	printf("thread 2 exiting ...\n");
	int* p = (int*)malloc(sizeof(int));
	*p = 2;
	pthread_exit((void*)p);
}

void* thread3(void* arg)
{
	while (1) { //
		printf("thread 3 is running ...\n");
		sleep(1);
	}
	return NULL;
}

int main(void)
{
	pthread_t tid;
	void* ret;

	// thread 1 return
	pthread_create(&tid, NULL, thread1, NULL);
	pthread_join(tid, &ret);
	printf("thread return, thread id %X, return code:%d\n", tid, *(int*)ret);
	free(ret);

	// thread 2 exit
	pthread_create(&tid, NULL, thread2, NULL);
	pthread_join(tid, &ret);
	printf("thread return, thread id %X, return code:%d\n", tid, *(int*)ret);
	free(ret);

	// thread 3 cancel by other
	pthread_create(&tid, NULL, thread3, NULL);
	sleep(3);
	pthread_cancel(tid);
	pthread_join(tid, &ret);
	if (ret == PTHREAD_CANCELED)
		printf("thread return, thread id %X, return code:PTHREAD_CANCELED\n", tid);
	else
		printf("thread return, thread id %X, return code:NULL\n", tid);
}

运行结果:
[root@localhost linux]# . / a.out
thread 1 returning ...
thread return, thread id 5AA79700, return code:1
thread 2 exiting ...
thread return, thread id 5AA79700, return code : 2
thread 3 is running ...
thread 3 is running ...
thread 3 is running ...
thread return, thread id 5AA79700, return code : PTHREAD_CANCELED

⚡(2)复杂版

复杂版涉及线程循环创建、等待、删除

  • makefile
mythread:mythread.cc
	g++ -o $@ $^ -lpthread -std=c++11  //-lpthread - 使用pthread库
.PHONY:clean
clean:
	rm -f mythread
  • mythread.cc
 #include <iostream>
 #include <cstdlib>
 #include <string>
 #include <cassert>
 #include <vector>
 #include <pthread.h>
 #include <unistd.h>

 using namespace std;

 // 当成结构体使用
 class ThreadData
 {
 public:
     int number;
     pthread_t tid;
     char namebuffer[64];
 };

 class ThreadReturn
 {
 public:
     int exit_code;
     int exit_result;
 };

 //1. start_routine, 现在是被几个线程执行呢?10, 这个函数现在是什么状态?重入状态
 //2. 该函数是可重入函数吗?是的!(可在任意时刻被打断,恢复运行时不会丢失数据)
 //3. 在函数内定义的变量,都叫做局部变量,具有临时性 -- 今天依旧适用
 //    -- 在多线程情况下, 也没有问题 -- 其实每一个线程都有自己独立的栈结构!
 void *start_routine(void *args)
 {
     ThreadData *td = static_cast<ThreadData *>(args); // 安全的进行强制类型转化
     int cnt = 10;
     while (cnt)
     {
         cout << "cnt: " << cnt << " &cnt: " << &cnt << endl; // bug
         cnt--;
         sleep(1);

         // return nullptr;     // 线程函数结束,return的时候,线程就算终止了
         // pthread_exit(nullptr);
         // exit(0); // 能不能用来终止线程,不能,因为exit是终止进程的!,任何一个执行流调用exit都会让整个进程退出

     }

     // 线程如何终止的问题
   
     // return (void*)td->number; // warning, void *ret = (void*)td->number;
     // return (void *)106;
     // pthread_exit((void*)111); 

     ThreadReturn * tr = new ThreadReturn();
     tr->exit_code = 1;
     tr->exit_result = 106;

     // ThreadReturn tr; // warning,在栈上开辟的空间 return &tr,退出后数据就拿不到了;

     //return (void*)tr; //右值
     return (void*)100;
 }

 int main()
 {
     // 1. 我们想创建一批线程
     vector<ThreadData*> threads;
 #define NUM 10
     for(int i = 0; i < NUM; i++)
     {
         ThreadData *td = new ThreadData();
         td->number = i+1;
         snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i+1);//将 "thread", i+1 放入namebuffer
         pthread_create(&td->tid, nullptr, start_routine, td);
         threads.push_back(td);

     }

     for(auto &iter : threads)
     {
         cout << "create thread: " << iter->namebuffer << " : " << iter->tid << " suceesss" << endl;
     }

     // 线程是可以被cancel取消的!注意:线程要被取消,前提是这个线程已经跑起来了
     // 线程如果是被取消的,退出码:-1 (PTHREAD_CANCELED)
     sleep(5);
     for(int i = 0; i < threads.size()/2; i++)
     {
         pthread_cancel(threads[i]->tid);   //主线程取消新线程
         cout << "pthread_cancel : " << threads[i]->namebuffer << " success" << endl;
     }

     for(auto &iter : threads) //进程等待
     {
         void *ret = nullptr; // 注意: 是void *哦
         // ? : 为什么没有见到,线程退出的时候,对应的退出信号??? 线程出异常,收到信号,整个进程都会退出!
         // pthread_join:默认就认为函数会调用成功!不考虑异常问题,异常问题是你进程该考虑的问题!
         int n = pthread_join(iter->tid, (void**)&ret); // void ** retp; *retp = return (void*)td->number
         assert(n == 0);
         cout << "join : " << iter->namebuffer << " success, exit_code: " << (long long)ret << endl;
         delete iter;
     }

     cout << "main thread quit " << endl;

     return 0;
 }
  • 运行结果

在这里插入图片描述

下面是一张另一张结果图哦!

在这里插入图片描述


☀️五、C++多线程引入

上面章节讲述的都是以C语言为基础的原生线程库应用,语言设计者们将其加以封装就形成了C++11的多线程

  • 代码示例
#include <iostream>
#include <unistd.h>
#include <thread>

void thread_run()
{
    while (true)
    {
        std::cout << "我是新线程..." << std::endl;
        sleep(1);
    }
}

int main()
{
    // 任何语言,在linux中如果要实现多线程,必定要是用pthread库
    // 如何看待C++11中的多线程呢??C++11 的多线程,在Linux环境中,本质是对pthread库的封装!
    std::thread t1(thread_run);

    while (true)
    {
        std::cout << "我是主线程..." << std::endl;
        sleep(1);
    }

    t1.join();

    return 0;
}
  • 运算结果

在这里插入图片描述


☀️结语

🌹🌹 多线程1 的知识大概就讲到这里啦,博主后续会继续更新更多C++ 和 Linux的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQL sever中的视图

目录 一、视图概述&#xff1a; 二、视图好处 三、创建视图 法一&#xff1a; 法二&#xff1a; 四、查看视图信息 五、视图插入数据 六、视图修改数据 七、视图删除数据 八、删除视图 法一&#xff1a; 法二&#xff1a; 一、视图概述&#xff1a; 视图是一种常用…

如何使用 Datree 避免 Kubernetes 错误配置

Kubernetes 是一个复杂的系统,具有许多移动部件。正确的配置规则对于您的服务可靠运行至关重要。当您在没有经过全面审查过程的情况下手动编写 Kubernetes 清单时,可能会出现错误。 Datree是一个基于规则的工具,可以自动查找清单中的问题。您可以使用它来发现策略违规行为,…

常见算法-洗扑克牌(乱数排列)

常见算法-洗扑克牌&#xff08;乱数排列&#xff09; 1、说明 洗扑克牌的原理其实与乱数排列是相同的&#xff0c;都是将一组数字&#xff08;例如1∼N&#xff09;打乱重新排列&#xff0c;只不过洗扑克牌多了一个花色判断的动作而已。 初学者通常会直接想到&#xff0c;随…

基于 ACK Fluid 的混合云优化数据访问(一):场景与架构

作者&#xff1a;车漾&#xff08;必嘫&#xff09; 本系列文章将介绍如何基于 ACK Fluid 支持和优化混合云的数据访问场景。 概述 在 AI 和大数据时代&#xff0c;算力即正义&#xff0c;强大的算力推动了源源不断的创新。然而&#xff0c;企业自建的算力集群存在资源容量和…

继续改进 换一种 使用 result 想直接用CourseExtend

改 c.cid cid, 表示 c.cid 在 from timetable tt inner join teacher t on tt.tidt.tid inner join course c on tt.cidc.cid where tt.cid#{cid} 查出来了 任何赋值给 后面那个cid t.tname "teacher.tname", 表示查出来 赋值给下图那个teacher类的对应属性…

解决:yarn 无法加载文件 “C:\Users\XXXXX\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本“ 的问题

1、问题描述&#xff1a; 报错的整体代码为&#xff1a; yarn : 无法加载文件 C:\Users\admin\AppData\Roaming\npm\yarn.ps1&#xff0c;因为在此系统上禁止运行脚本 // 整体的报错代码为 &#xff1a; yarn : 无法加载文件 C:\Users\admin\AppData\Roaming\npm\yarn.ps1&…

HarmonyOS/OpenHarmony原生应用开发-华为Serverless云端服务支持说明(一)

云端服务的实现是HarmonyOS/OpenHarmony原生应用开发的一个重要的环节&#xff0c;如果用户端是鸿蒙原生应用&#xff0c;但是服务端即云端还是基于传统的各种WEB网络框架、数据库与云服务器&#xff0c;那么所谓的原生应用开发实现的数据即后端服务是和以前、现在的互联网、移…

线性代数中涉及到的matlab命令-第一章:行列式

目录 1&#xff0c;逆序数 2&#xff0c;行列式定义和性质 2.1&#xff0c;常用特性及命令 2.2&#xff0c;求行列式 2.3&#xff0c;行列式的性质 2&#xff0c;行列式按行&#xff08;列&#xff09;展开 3&#xff0c;范德蒙德行列式 在学习线性代数过程中&#…

ssm172基于SSM的旅行社管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

python每日一练(3)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

安装torchtext遇到的坑及解决办法

刚开始秉着需要什么就pip install什么的原则直接pip install torchtext&#xff0c;结果&#xff1a; 把我这个环境打乱了&#xff0c;自作主张的删掉之前的很多包重新安装了其他版本的包而不是自适应的安装当前torch所对应的torchtext。因为这个环境比较重要也用在其他的工程…

【办公-excel】两个时间相减 (二) - 带毫秒的时间进行相减操作

一、使用内部函数 1.1 效果展示 TEXT(((RIGHT(TEXT(B2,"yyyy-mm-dd hh:mm:ss.000"),LEN(TEXT(B2,"yyyy-mm-dd hh:mm:ss.000"))-FIND(".",TEXT(B2,"yyyy-mm-dd hh:mm:ss.000")))-RIGHT(TEXT(A2,"yyyy-mm-dd hh:mm:ss.000"),…

力扣刷题 day38:10-08

1.矩阵中战斗力最弱的 K 行 给你一个大小为 m * n 的矩阵 mat&#xff0c;矩阵由若干军人和平民组成&#xff0c;分别用 1 和 0 表示。 请你返回矩阵中战斗力最弱的 k 行的索引&#xff0c;按从最弱到最强排序。 如果第 i 行的军人数量少于第 j 行&#xff0c;或者两行军人数…

数据结构——哈希的应用之位图,布隆过滤器与哈希切割

文章目录 前言1. 位图1.1 位图的概念1. 2 模拟实现stl位图位图的应用 2.布隆过滤器2.1 布隆过滤器的概念 布隆过滤器的查找布隆过滤器的删除问题布隆过滤器优点布隆过滤器缺陷 哈希切割 前言 本篇博客主要讲述的是应用哈希的一些数据结构_位图和布隆过滤器&#xff0c;讲解了这…

常见算法-三色棋(Gossip)

常见算法-三色棋&#xff08;Gossip&#xff09; 1、说明 三色旗的问题最早由E.W.Dijkstra所提出&#xff0c;他所使用的用语为Dutch Nation Flag(Dijkstra为荷兰人)&#xff0c;而多数的作者则使用Three-Color Flag来称之。 假设有一条绳子&#xff0c;上面有红、白、蓝三种…

OpenCV3-Python(7)模板匹配和霍夫检测

模板匹配 膜版匹配不能匹配尺度变换和视角变换的图像 图片中查找和模板相似度最高的图像 计算相似程度最高的位置 res cv.matchTemplate(img , template, method) 该方法返回一个类似灰度图的东西&#xff0c;如果用的相关匹配&#xff0c;那么亮的地方就是可能匹配上的地方 …

【高等の数学】e^-3x的一阶导数

一、直接公式法 对于指数函数 f(x) e^g(x)&#xff0c;其中 g(x) 是关于 x 的函数&#xff0c;导数 f(x) 可以表示为 f(x) g(x) * e^g(x)。 在我们的情况下&#xff0c;g(x) -3x。 我们先求导 g(x) &#xff08;-3x&#xff09; -3。因此&#xff0c;我们有 g(x) -3。 现…

基于OpenCV设计的流媒体播放器(RTSP、RTMP)

一、前言 随着互联网的普及和发展,流媒体技术已成为日常生活中不可或缺的一部分。流媒体播放器作为流媒体技术的重要组成部分,其性能和功能直接影响到用户的观影体验。本文介绍使用OpenCV和Qt设计一款流媒体播放器,专门用于播放直播视频流,例如RTSP、RTMP。该播放器只播放…

【深度学习】UniControl 一个统一的扩散模型用于可控的野外视觉生成

论文&#xff1a;https://arxiv.org/abs/2305.11147 代码&#xff1a;https://github.com/salesforce/UniControl#data-preparation docker快速部署&#xff1a;https://qq742971636.blog.csdn.net/article/details/133129146 文章目录 AbstractIntroductionRelated WorksUniCo…

【微服务】八. 统一网关gateway

8.1 网关作用介绍 网关功能&#xff1a; 身份认证和权限校验服务路由、负载均衡请求限流 网关的技术实现 在SpringCloud中网关的实现包括两种&#xff1a; gatewayzuul Zuul是基于Servlet的实现&#xff0c;属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的Web…