线程池实现

news2024/11/26 0:48:53

一、线程池介绍

1)应用场景

当并发数很多的时候,并且每个线程执行时间很短的任务,这样就会频繁创建线程,而这样的频繁创建和销毁线程会大大降低系统的执行效率。对于这种场景我们可以使用线程池来复用之前创建的线程,降低线程的频繁创建和销毁工作,达到提高执行效率的目的。

2)线程池原理

线程池使用者往线程池任务队列里面添加任务,线程池会根据任务的多少来自动创建或销毁工作线程取执行任务,即当任务数量比较多而线程池比较少处于忙不过来的状态时,线程池就会自动创建线程,而当仍务数量比较少而空闲线程比较多时,线程池就会自动销毁一部分空闲线程。其中任务队列、线程池使用者和工作线程组成一个生产者消费者模型,线程池使用者(消费者)检查队列已满就阻塞,否则就向任务队列添加任务并通知工作线程(消费者)取任务执行,而工作线程(消费者)取任务之后也会向线程池使用者(生产者)发送通知解阻塞。

3)线程池结构

线程池由任务队列工作线程管理线程三部分组成,他们的所用分别如下。

  • 任务队列
    • 负责保存要执行的任务(一般每个任务就是一个回调函数);
    • 线程池使用者(生产者)往任务队列里面添加任务,并通知工作线程(消费者)取任务执行;
    • 工作线程(消费者)从任务队列里面获取到任务后,需要把该任务从队列中删除;
  • 工作线程
    • 负责执行任务队列里面的任务;
    • 当任务队列没有任务时,工作线程便自动睡眠防止占用CPU资源;
    • 当由任务时唤醒工作线程,从队列中取任务执行(从队列中取出任务后,如果生产者此时阻塞的话可以通知生产者解阻塞);
  • 管理线程
    • 负责控制工作线程的数量;
    • 当空闲的工作线程数量比较多时,就销毁一部分线程;
    • 当队列任务比较多而工作线程比较少时,新创建一部分线程;

线程池结构图

二、程序实现

1)C语言实现

threadPool.h

#ifndef _THREAD_POOL_
#define _THREAD_POOL_

typedef struct ThreadPool ThreadPool;

// 创建并初始化线程池
ThreadPool* threadPoolCreate(int queueSize, int minNum, int maxNum);

// 销毁线程池
void threadPoolDestory(ThreadPool* pool);

// 往线程池添加任务
int threadPoolAdd(ThreadPool* pool, void (*handler)(void* arg), void* arg);

// 获取线程池当前工作线程数
int threadPoolWorkNum(ThreadPool* pool);

// 获取线程池当前存活线程数
int threadPoolLiveNum(ThreadPool* pool);

#endif // _THREAD_POOL_

threadPool.c

#include "threadPool.h"
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>
#include <unistd.h>

#define CHAGNUM 4

void* worker(void *arg);
void* manager(void *arg);
void threadExit(ThreadPool* pool);

typedef struct Task {
    void (*handler)(void* arg);
    void* arg;
}Task;

struct ThreadPool {
    Task* taskQ;
    int qCapacity;
    int qSize;
    int qFront;
    int qBack;

    pthread_t manageID;
    pthread_t* workIDs;
    int maxNum;			// 最大线程数量
    int minNum;			// 最小线程数量
    int workNum;		// 正在执行任务的工作线程数量
    int liveNum;        // 当前已创建的的工作线程数量
    int exitNum;        // 需要销毁退出的线程数量

    pthread_mutex_t mutexPool;
    pthread_mutex_t mutexWork; // 锁workNum变量
    pthread_cond_t hasTask;      // 任务队列是否有任务
    pthread_cond_t isFull;       // 任务队列是否已满

    int isDestory; // 线程池是否销毁
};

ThreadPool* threadPoolCreate(int queueSize, int minNum, int maxNum)
{
    int i, res = 0;

    // 创建线程池对象
    ThreadPool* tPool = (ThreadPool*)malloc(sizeof(struct ThreadPool));
    if (tPool == NULL) {
        perror("tPool malloc:");
        goto err;
    }

    // 创建任务队列
    tPool->taskQ = (Task*)malloc(sizeof(struct Task) * queueSize);
    if (tPool->taskQ == NULL) {
        perror("taskQ malloc:");
        goto err;
    }
    tPool->qSize = 0;
    tPool->qCapacity = queueSize;
    tPool->qFront = tPool->qBack = 0;

    // 创建存储工作线程ID的数组
    tPool->workIDs = (pthread_t*)malloc(sizeof(pthread_t) * maxNum);
    if (tPool->workIDs == NULL) {
        perror("workIDs malloc:");
        goto err;
    }
    memset(tPool->workIDs, 0, sizeof(pthread_t) * maxNum);
    tPool->maxNum = maxNum;
    tPool->minNum = minNum;
    tPool->workNum = 0;
    tPool->liveNum = minNum;
    tPool->exitNum = 0;

    tPool->isDestory = 0;
    // 初始化互斥量和条件变量
    if (pthread_mutex_init(&tPool->mutexPool, NULL) != 0 ||
        pthread_mutex_init(&tPool->mutexWork, NULL) != 0 ||
        pthread_cond_init(&tPool->isFull, NULL) != 0 ||
        pthread_cond_init(&tPool->hasTask, NULL) != 0) {
        printf("mutex or cond init fail...\n");
        goto err;
    }
    

    // 创建工作线程
    for (i = 0; i < minNum; i++) {
        res = pthread_create(&tPool->workIDs[i], NULL, worker, tPool);
        if (res != 0) {  
            printf("thread create failed for worker, errno: %d, idx: %d\n", res, i);
            goto err;
        }
    }

    // 创建管理线程
    res = pthread_create(&tPool->manageID, NULL, manager, tPool);
    if (res != 0) {
        printf("thread create failed for manager, errno: %d\n", res);
        goto err;
    }

    return tPool;

err:
    if (tPool && tPool->taskQ) {
        free(tPool->taskQ);
        tPool->taskQ = NULL;
    }

    if (tPool && tPool->workIDs) {
        free(tPool->workIDs);
        tPool->workIDs = NULL;
    }

    if (tPool) {
        free(tPool);
    }
    return NULL;
}

void* worker(void *arg)
{
    Task task;
    ThreadPool* pool = (ThreadPool*)arg;

    while(1) {
        pthread_mutex_lock(&pool->mutexPool);

        // 队列为空就阻塞当前线程,避免占用CPU
        while(pool->qSize == 0 && !pool->isDestory) {
            pthread_cond_wait(&pool->hasTask, &pool->mutexPool);
            
            // 减少空闲线程
            if (pool->exitNum > 0) {
                pool->exitNum--;
                if (pool->liveNum > pool->minNum) {
                    pool->liveNum--;
                    pthread_mutex_unlock(&pool->mutexPool);
                    threadExit(pool);
                }
            }
        }

        // 销毁线程池
        if (pool->isDestory) {
            pool->liveNum--;
            pthread_mutex_unlock(&pool->mutexPool);
            threadExit(pool);
        }

        // 取一个任务执行
        task.arg = pool->taskQ[pool->qFront].arg;
        task.handler = pool->taskQ[pool->qFront].handler;
        pool->qFront = (pool->qFront + 1) % pool->qCapacity;
        pool->qSize--;
        pthread_cond_signal(&pool->isFull);
        pthread_mutex_unlock(&pool->mutexPool);

        pthread_mutex_lock(&pool->mutexWork);
        pool->workNum++;
        pthread_mutex_unlock(&pool->mutexWork);
        task.handler(task.arg);
        if (task.arg) {  // 释放资源 或者 用户在回调函数中释放这里就不释放了
            free(task.arg);
            task.arg = NULL;
        }

        pthread_mutex_lock(&pool->mutexWork);
        pool->workNum--;
        pthread_mutex_unlock(&pool->mutexWork);
    }

    return NULL;
}

void* manager(void *arg)
{
    int i = 0, incNum = CHAGNUM;
    ThreadPool* pool = (ThreadPool*)arg;

    while(!pool->isDestory) {
        sleep(3);
        pthread_mutex_lock(&pool->mutexPool);
        int queueSize = pool->qSize;
        int liveNum = pool->liveNum;
        pthread_mutex_unlock(&pool->mutexPool);

        pthread_mutex_lock(&pool->mutexWork);
        int workNum = pool->workNum;
        pthread_mutex_unlock(&pool->mutexWork);

        // 数据处理不过来要增加线程
        if (queueSize > liveNum) {
            pthread_mutex_lock(&pool->mutexPool);
            for(i = 0; i < pool->maxNum && incNum > 0; i++) {
                if (pool->workIDs[i] == 0) {
                    pthread_create(&pool->workIDs[i], NULL, worker, pool);
                    incNum--;
                    pool->liveNum++;
                    printf("new thread %ld, liveNum = %d, workNum = %d\n",
                        pool->workIDs[i], pool->liveNum, pool->workNum);
                }
            }
            pthread_mutex_unlock(&pool->mutexPool);
        }
        
        // 空闲线程多了要销毁
        if(workNum * 2 < liveNum &&
            liveNum - CHAGNUM > pool->minNum) {

            pthread_mutex_lock(&pool->mutexPool);
            pool->exitNum = CHAGNUM;
            pthread_mutex_unlock(&pool->mutexPool);

            for (i = 0; i < CHAGNUM; i++) {
                pthread_cond_signal(&pool->hasTask);
            }
        }
    }
    return NULL;
}

int threadPoolAdd(ThreadPool* pool, void (*handler)(void* arg), void* arg)
{
    pthread_mutex_lock(&pool->mutexPool);
    while(pool->qSize == pool->qCapacity && !pool->isDestory)  {
        pthread_cond_wait(&pool->isFull, &pool->mutexPool);
    }
    if (pool->isDestory) {
        pthread_mutex_unlock(&pool->mutexPool);
        return -1;
    }

    pool->taskQ[pool->qBack].arg = arg;
    pool->taskQ[pool->qBack].handler = handler;
    pool->qBack = (pool->qBack + 1) % pool->qCapacity;
    pool->qSize++;
    pthread_cond_signal(&pool->hasTask); // 通知空闲的工作线程取任务执行
    pthread_mutex_unlock(&pool->mutexPool);
    return 0;
}

void threadExit(ThreadPool* pool)
{
    int i;
    pthread_t tid = pthread_self();
    for(i = 0; i < pool->maxNum; i++) {
        if (pool->workIDs[i] == tid) {
            pool->workIDs[i] = 0;
            break;
        }
    }
    printf("thread %ld exit, liveNum = %d, workNum = %d\n",
        tid, pool->liveNum, pool->workNum);
    pthread_exit(0);
}

int threadPoolWorkNum(ThreadPool* pool)
{
    int workNum;

    pthread_mutex_lock(&pool->mutexWork);
    workNum = pool->workNum;
    pthread_mutex_unlock(&pool->mutexWork);

    return workNum;
}

int threadPoolLiveNum(ThreadPool* pool)
{
    int liveNum;

    pthread_mutex_lock(&pool->mutexPool);
    liveNum = pool->liveNum;
    pthread_mutex_unlock(&pool->mutexPool);

    return liveNum;
}

void threadPoolDestory(ThreadPool* pool)
{
    int i;

    if (pool == NULL) {
        return;
    }
    pool->isDestory = 1;
    // 销毁管理线程
    pthread_join(pool->manageID, NULL);

    // 销毁工作线程
    for (i = 0; i < pool->maxNum; i++) {
        if (pool->workIDs[i] > 0) {
            pthread_cond_signal(&pool->hasTask);
        }
    }
    for (i = 0; i < pool->maxNum; i++) {
        if (pool->workIDs[i] > 0) {
            pthread_join(pool->workIDs[i], NULL);
        }
    }

    pthread_mutex_destroy(&pool->mutexPool);
    pthread_mutex_destroy(&pool->mutexWork);
    pthread_cond_destroy(&pool->hasTask);

    if (pool->workIDs) {
        free(pool->workIDs);
        pool->workIDs = NULL;
    }
    if (pool->taskQ) {
        free(pool->taskQ);
        pool->taskQ = NULL;
    }
    free(pool);
    printf("thread pool destory...\n");
}

main.c

#include "threadPool.h"
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

void myTest(void *arg)
{
    printf("tid: %ld, num = %d\n", pthread_self(), *(int *)arg);
    sleep(3);
}

int main()
{
    int i;
    ThreadPool *pool = threadPoolCreate(20, 4, 10);
    for (i = 0; i < 40; i++) {
        int* num = (int *)malloc(sizeof(int));
        *num = i;
        threadPoolAdd(pool, myTest, num);
    }
    sleep(10);
    threadPoolDestory(pool);
    return 0;
}

2)C++实现

threadPool.h

#ifndef _THREADPOOL_H
#define _THREADPOOL_H
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <queue>

struct Task  {
    void (*handler)(void*);
    void* arg = nullptr;
};

class TaskQueue {
public:
    TaskQueue();
    ~TaskQueue();

    // 添加任务
    void addTask(Task& task);
    void addTask(void (*handler)(void*), void* arg);

    // 取出任务
    Task getTask();

    // 获取任务数
    inline int getTaskNum();

private:
    pthread_mutex_t m_lock;
    std::queue<Task> m_que;
};

class ThreadPool {
public:
    ThreadPool(int max, int min);
    ~ThreadPool();

    // 添加任务
    void addTask(Task task);

    // 获取工作线程数
    int getWorkNum();

    // 获取存活线程数
    int getLiveNum();

private:
    static void* worker(void* arg);
    static void* manager(void* arg);

    void threadExit();

private:
    TaskQueue m_taskQ;
    int m_maxNum;
    int m_minNum;
    int m_workNum;
    int m_liveNum;
    int m_exitNum;
    static const int m_changeNum = 2;

    bool m_isDestory = false;

    pthread_t m_managerTid;
    pthread_t* m_workTids;

    pthread_cond_t m_hasTask;
    pthread_mutex_t m_lock; // 锁m_workNUm、m_liveNum、m_exitNum变量
};

#endif // _THREADPOOL_H

threadPool.cpp

#include "threadPool.h"
#include <iostream>

ThreadPool::ThreadPool(int max, int min)
{
    int i;
    m_maxNum = max;
    m_minNum = min;
    m_workNum = 0;
    m_liveNum = min;
    m_exitNum = 0;

    if (pthread_cond_init(&m_hasTask, nullptr) != 0
        || pthread_mutex_init(&m_lock, nullptr) != 0) {
            std::cout << "cond or mutex init fail..." << std::endl;
            return;
    }

    m_workTids = new pthread_t[m_maxNum];
    if(m_workTids == nullptr) {
        std::cout << "m_workTids malloc failed..." << std::endl;
    }
    memset(m_workTids, 0, sizeof(pthread_t) * m_maxNum);
    
    // 创建工作线程
    for (i = 0; i < m_minNum; i++) {
        pthread_create(&m_workTids[i], nullptr, worker, this); 
        std::cout << "worker thread " << m_workTids[i] << " created" << std::endl;
    }

    // 创建管理线程
    pthread_create(&m_managerTid, nullptr, manager, this);
}

ThreadPool::~ThreadPool()
{
    m_isDestory = true;

    pthread_join(m_managerTid, nullptr);

    pthread_cond_broadcast(&m_hasTask);

    for (int i = 0; i < m_maxNum; i++) {
        if (m_workTids[i] != 0) {
            pthread_join(m_workTids[i], nullptr);
            std::cout << "thread i = " << i << " tid = " << m_workTids[i] << " exit..." << std::endl;
            m_workTids[i] = 0;
        }
    }

    pthread_mutex_destroy(&m_lock);
    pthread_cond_destroy(&m_hasTask);

    if (m_workTids) {
        delete []m_workTids;
    }
    std::cout << "liveNum = "<< m_liveNum <<", workNum = "<< m_workNum <<", queSize = " << this->m_taskQ.getTaskNum() << std::endl;
}

void* ThreadPool::worker(void* arg)
{
    ThreadPool* pool = static_cast<ThreadPool*>(arg);

    while(1) {
        pthread_mutex_lock(&pool->m_lock);
        while(pool->m_taskQ.getTaskNum() == 0 && !pool->m_isDestory) {
            std::cout << "thread " << pthread_self() << " waitting..." << std::endl;
            pthread_cond_wait(&pool->m_hasTask, &pool->m_lock);
            // 空闲线程退出
            if (pool->m_exitNum > 0) {
                pool->m_exitNum--;
                if(pool->m_liveNum > pool->m_minNum) { 
                    pool->m_liveNum--;
                    pthread_mutex_unlock(&pool->m_lock);
                    pool->threadExit();
                }
            }
        }

        // 销毁线程池
        if (pool->m_isDestory) {
            pool->m_liveNum--;
            pthread_mutex_unlock(&pool->m_lock);
            pthread_exit(0);  // 这里不调用threadExit是让主线程好回收资源
        }
        // 取任务执行
        Task task = pool->m_taskQ.getTask();
        pool->m_workNum++;
        pthread_mutex_unlock(&pool->m_lock);
        task.handler(task.arg);  // 用户自己取释放arg内存
        pthread_mutex_lock(&pool->m_lock);
        pool->m_workNum--;
        pthread_mutex_unlock(&pool->m_lock);

    }
    return nullptr;
}

void* ThreadPool::manager(void* arg)
{
    ThreadPool* pool = static_cast<ThreadPool*>(arg);

    while(!pool->m_isDestory) {
        sleep(3);

        int liveNum;
        int taskNum;
        int workNum;
        int i, incNum = pool->m_changeNum;

        pthread_mutex_lock(&pool->m_lock);
        liveNum = pool->m_liveNum;
        workNum = pool->m_workNum;
        taskNum = pool->m_taskQ.getTaskNum();
        pthread_mutex_unlock(&pool->m_lock);

        // 任务太多忙不过来需要创建线程
        if(!pool->m_isDestory && taskNum > liveNum && liveNum < pool->m_maxNum) {
            for (i = 0; i < pool->m_maxNum && incNum > 0 ; i++) {
                pthread_mutex_lock(&pool->m_lock);
                if (pool->m_workTids[i] == 0) {
                    pool->m_liveNum++;
                    incNum--;
                    pthread_create(&pool->m_workTids[i], NULL, worker, pool);
                    std::cout << "new thread " << pool->m_workTids[i] << " created" << std::endl;
                }
                pthread_mutex_unlock(&pool->m_lock);
            }
        }

        // 销毁多余的空闲线程
        incNum = pool->m_changeNum;
        if (!pool->m_isDestory && workNum * 2 < liveNum && liveNum > pool->m_minNum) {
            pthread_mutex_lock(&pool->m_lock);
            pool->m_exitNum = pool->m_changeNum;
            pthread_mutex_unlock(&pool->m_lock);
            while (incNum--) {
                pthread_cond_signal(&pool->m_hasTask);
            }
        }
    }
    return nullptr;
}

void ThreadPool::addTask(Task task)
{
    if (m_isDestory) {
        return;
    }
    pthread_mutex_lock(&m_lock);
    m_taskQ.addTask(task);
    pthread_mutex_unlock(&m_lock);
    pthread_cond_signal(&m_hasTask);
}

void ThreadPool::threadExit()
{
    for (int i = 0; i < m_maxNum; i++) {
        if (m_workTids[i] == pthread_self()) {
            std::cout << "thread " << m_workTids[i] << " exit..." << std::endl;
            pthread_mutex_lock(&m_lock);
            m_workTids[i] = 0;
            pthread_mutex_unlock(&m_lock);
            pthread_exit(0);
        }
    }
}

int ThreadPool::getWorkNum()
{
    int workNum = 0;
    pthread_mutex_lock(&m_lock);
    workNum = m_workNum;
    pthread_mutex_unlock(&m_lock);
    return m_workNum;
}

int ThreadPool::getLiveNum()
{
    int liveNum = 0;
    pthread_mutex_lock(&m_lock);
    liveNum = m_liveNum;
    pthread_mutex_unlock(&m_lock);
    return liveNum;
}

TaskQueue::TaskQueue() 
{
    pthread_mutex_init(&m_lock, NULL);
}

TaskQueue::~TaskQueue() 
{
    pthread_mutex_destroy(&m_lock);
}

void TaskQueue::addTask(Task& task)
{
    pthread_mutex_lock(&this->m_lock);
    m_que.push(task);
    pthread_mutex_unlock(&this->m_lock);
}

void TaskQueue::addTask(void (*handler)(void*), void* arg)
{
    Task task;
    task.arg = arg;
    task.handler = handler; 
    pthread_mutex_lock(&this->m_lock);
    m_que.push(task);
    pthread_mutex_unlock(&this->m_lock);
}

Task TaskQueue::getTask()
{
    Task task;

    pthread_mutex_lock(&this->m_lock);
    if (m_que.size() > 0) {
        task = m_que.front();
        m_que.pop();
    }
    pthread_mutex_unlock(&this->m_lock);

    return task;
}

inline int TaskQueue::getTaskNum()
{
    return this->m_que.size();
}

main.cpp

#include "threadPool.h"
#include <iostream>
using namespace std;
 
void my_test(void* arg)
{
    int num = *(int*)arg;
    cout << "thread id: " << pthread_self() << " , num: " << num << endl;
    sleep(1);
    delete (int*)arg;
}

int main()
{
    ThreadPool* pool = new ThreadPool(10, 4);
    sleep(1);
    for (int i = 0; i < 30; i++) {
        Task task;
        task.handler = my_test;
        task.arg = new int(i);
        pool->addTask(task);
    }
    sleep(10);
    delete pool;
    return 0;
}

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

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

相关文章

Linux—基础篇:目录结构

1、基本介绍 1、linux的文件系统是采用级层式的树状目录结构&#xff0c;在此结构的最上层是根目录“/”,然后在此目录下创建其他目录 2、在Linux的世界里&#xff0c;一切皆文件&#xff01;&#xff01;&#xff01; 2、具体的目录结构 不用背&#xff0c;知道即可 2.1、…

价值1000元的稀有二开版的无限坐席在线客服系统源码+教程

demo软件园每日更新资源,请看到最后就能获取你想要的: 1.价值1000元的稀有二开版的无限坐席在线客服系统源码教程 价值1000元的稀有二开版的无限坐席在线客服系统源码 直接一键安装的&#xff0c;启动两个端口就行了&#xff0c;安装倒是简单 类型&#xff1a;在线客服系统 …

MathType7精简版数学公式编辑器

许多简单的数学公式&#xff0c;我们可以使用输入法一个个找到特殊符号并输入&#xff0c;但是对于高等数学中较多复杂的公式符号&#xff0c;是很难使用输入法完成的。那么&#xff0c;我们就需要借助公式编辑器&#xff0c;这里推荐一款我自己正在使用的MathType。 MathType是…

Redis单机数据库

文章目录 一、Redis数据库Redis数据库redisDb数据库键空间——dict过期字典——expires设置键的生存时间移除键的过期时间返回键的生存时间 Redis的过期删除策略1、定期删除2、惰性删除3、内存淘汰机制 过期键处理1、RDB功能对过期键的处理2、AOF功能对过期键的处理3、复制功能…

chatgpt赋能python:Python隐藏变量:探秘程序内部的“奥秘”

Python 隐藏变量&#xff1a;探秘程序内部的“奥秘” 作为一门高级编程语言&#xff0c;Python备受程序员的欢迎。但是&#xff0c;你是否了解Python隐藏变量&#xff1f;这些看似神秘的变量对程序员的编码工作有哪些影响呢&#xff1f;本篇文章将为您详细介绍Python隐藏变量的…

Hive 实现ACID

Hive官方提示&#xff0c;Hive在版本0.14之后可以支持对表数据的UPDATE和DELETE&#xff1a; 具体操作如下&#xff1a; -- 环境参数设置 set hive.support.concurrencytrue; set hive.txn.managerorg.apache.hadoop.hive.ql.lockmgr.DbTxnManager;-- 建表&#xff0c;以ORC的…

【MySQL新手到通关】第六章 时间日期函数

文章目录 1.获取日期时间函数1.1 获取当前日期时间1.2 获取当前日期1.3 获取当前时间 2.日期格式化★★★2.1 日期转指定格式字符串2.2 字符串转日期 3.日期间隔3.1 增加日期间隔 ★★★3.2 减去一个时间间隔★★★3.3 日期相差天数&#xff08;天&#xff09;3.4 相差时间&…

VSLAM视觉里程计总结

相机模型是理解视觉里程计之前的基础。视觉里程计&#xff08;VIO&#xff09;主要分为特征法和直接法。如果说特征点法关注的是像素的位置差&#xff0c;那么&#xff0c;直接法关注的则是像素的颜色差。特征点法通常会把图像抽象成特征点的集合&#xff0c;然后去缩小特征点之…

Docker容器 和 Kubernetes容器集群管理系统

一、快速了解Docker 1. 什么是Docker的定义 Docker 是一个开源的应用容器引擎&#xff0c;基于Go语言并遵从 Apache2.0 协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以…

C#,码海拾贝(23)——求解“复系数线性方程组“的“全选主元高斯消去法“之C#源代码,《C#数值计算算法编程》源代码升级改进版

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 求解线性方程组的类 LEquations /// 原作 周长发 /// 改编 深度混淆 /// </summary> public static partial class LEquations { /// <summary&g…

【C++】利用游戏壳实现飞机大战(设计类图、开发实现)

文章目录 飞机大战飞机大战类图分析背景类&#xff08;CBackGround&#xff09;成员属性成员函数 程序类&#xff08;CPlaneApp&#xff09;成员属性成员函数 玩家类&#xff08;CPlayer&#xff09;成员属性成员函数 炮弹类&#xff08;CGunner&#xff09;成员属性成员函数 炮…

AI周报-一周发生两次Ai事件;DragGAN 问世

&#x1f680; AI 图像编辑技术 DragGAN 问世&#xff0c;用户可以通过拖拽改变汽车大小或人物表情等 近日&#xff0c;马克斯・普朗克计算机科学研究所研究者们推出了一种控制GAN的新方法DragGAN&#xff0c;用户可以通过拖拽改变汽车大小或人物表情等。 DragGAN类似于Photo…

Rk1126 实现 yolov5 6.2 推理

基于 RK1126 实现 yolov5 6.2 推理. 转换 ONNX python export.py --weights ./weights/yolov5s.pt --img 640 --batch 1 --include onnx --simplify 安装 rk 环境 安装部分参考网上, 有很多. 参考: https://github.com/rockchip-linux/rknpu 转换 RK模型 并验证 yolov562_t…

企业想提高商机转化率该如何挑选CRM系统

CRM客户管理系统可以帮助销售人员跟踪和分析潜在客户的需求、行为和偏好&#xff0c;制定合适的销售策略&#xff0c;提高商机转化率。下面我们就来说说&#xff0c;CRM系统如何加速销售商机推进。 1、跟踪客户和动态 Zoho CRM可以帮助您记录和分析客户的需求、行为和偏好&am…

8 年 SQL 人,撑不过前 6 题

抱歉各位&#xff0c;标题党了。。 前两天发布了一款 SQL 题集&#xff1a; 开发了一个SQL数据库题库小程序 <<- 戳它直达 群里小伙伴反馈&#xff0c;太简单&#xff1a; 于是&#xff0c;我又改版了下&#xff1a; 列举几题&#xff0c;大家看看难度&#xff1a; SQL S…

Python类的成员介绍

Python类的成员介绍 在Python中&#xff0c;类&#xff08;class&#xff09;是一种定义对象的模板。对象是由类创建的实例&#xff0c;它们具有属性和方法。属性是对象的变量&#xff0c;而方法是对象的函数。 定义在类中的变量也称为属性&#xff0c;定义在类中的函数也称为方…

DragGAN:interactive point-based manipulation on the generative image manifold

AI绘画可控性研究与应用清华美院课程的文字稿和PPThttps://mp.weixin.qq.com/s?__bizMzIxOTczNjQ2OQ&mid2247484794&idx1&sn3556e5c467512953596237d71326be6e&chksm97d7f580a0a07c968dedb02d0ca46a384643e38b51b871c7a4f89b38a04fb2056e084167be05&scene…

基于html+css的图展示97

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

测量平差实习心得精选三篇(合集)

测量平差实习心得精选三篇 测量平差实习心得一为期两周的实习在不断地学习、尝试、修正的过程中圆满结束了。这次实习让我对许多问题有了深刻的认识。我认识到编程的重要性&#xff0c;认识到自学能力的重要性&#xff0c;认识到从书本到实践还有很长一段路要走。 熟练掌握一门…

探索C++非质变算法:如何更高效地处理数据

前言 &#x1f4d6;欢迎大家来到小K的c专栏&#xff0c;本节将为大家带来C非质变算法的详解 &#x1f389;欢迎各位→点赞&#x1f44f; 收藏&#x1f49e; 留言&#x1f514;​ &#x1f4ac;总结&#xff1a;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指…