当前位置: 首页 > news >正文

【Linux】多线程

目录

线程

线程和进程的关系

虚拟地址与物理地址的映射(三级映射)

​编辑

线程的使用

线程安全

 临界资源和临界区

互斥锁

同步与互斥

 互斥锁的使用

锁的初始化

加锁与解锁

信号量

信号与信号量的区别

 信号量的使用

Lock_guard与unique_lock

线程池

原理与作用

完整代码


线程

线程和进程的关系

线程在进程内部运行,是linux下调度的最小执行单位。

进程是承担分配系统资源的基本实体。

         但在Linux中严格来说不存在线程,只有轻量级进程(Linux中的线程和进程本质上是轻量级进程)。

        PCB(进程控制块),在Linux上具体表现为task_struct结构体,每个轻量级进程都有一个task_struct进行管理,所以一个PCB是<=一个进程的,为什么会出现一个PCB是<一个进程的情况?因为当线程数量大于1时,进程就会退化为主线程。

         多线程就是将正文代码的每一部分分别交给多个线程去执行。既然代码存储在内存中那代码必然是有存储地址的(这个地址是虚拟地址)。所以虚拟地址也是一种资源,而多线程执行不同的代码就是因为分配到了不同的资源。

 

虚拟地址与物理地址的映射(三级映射)

         对于一个32位的地址,如果直接通过一个页表进行映射则会非常浪费空间,于是在设计上将虚拟地址拆成10,10,12位。查询时先根据前10位虚拟地址从页目录中找到对应页表,再通过虚拟地址中间10位确定映射哪一个页框,最后加上虚拟地址最后12位偏移量就可以访问物理内存每一个地址了

线程的使用

pthread_t在Linux下是无符号整数(%lu)。

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)

  • 创建一个新线程,成功返回0,失败返回错误号 。

int pthread_join(pthread_t thread, void **retval

  • 等待线程结束,回收线程返回的信息。retval用于存储线程结束状态,简单地说就是存储返回值(不关心返回的结束状态可以设为nullptr)。

 注意线程调用的函数类型必须为返回值为void*参数为void*的函数。

void *threadStart(void *args)
{//将args强转成char*类型std::string name = static_cast<char *>(args);while (true){sleep(1);std::cout << name << " is running..." << std::endl;}//返回一个void*类型的错误码return (void*)1;
}int main()
{pthread_t tid;int n = pthread_create(&tid, nullptr, threadStart, (void *)"thread-1");if (n != 0){std::cout << "pthread_create error" << std::endl;return 1;}//用void *类型参数接收错误码void *code = nullptr;//等待线程tid结束n = pthread_join(tid, &code);if (n == 0){std::cout << "main thread wait success, newthread exit code is " <<         (uint64_t)code << std::endl;//(强转类型后)打印错误码}return 0;
}

pthread_t pthread_self() 

  • 获取线程id,成功返回0,失败无返回值。

gettid 获取的是内核中真实线程ID,  对于多线程进程来说,每个tid实际是不一样的,而pthread_self获取的是相对于进程的线程控制块的首地址, 只是用来描述统一进程中的不同线程。

void *threadStart(void *args)
{std::string name = static_cast<char *>(args);while (true){sleep(1);//对比getpid()和pthread_self()std::cout << name << " is running..." << ", getpid(): " << getpid() << " , pthread_self():" << pthread_self() << std::endl;}return (void*)1;
}int main()
{pthread_t tid;int n = pthread_create(&tid, nullptr, threadStart, (void *)"thread-1");if (n != 0){std::cout << "pthread_create error" << std::endl;return 1;}void *code = nullptr;n = pthread_join(tid, &code);if (n == 0){std::cout << "main thread wait success, newthread exit code is " <<         (uint64_t)code << std::endl;}return 0;
}

 

void pthread_exit(void *retval)

  • 将单个线程退出(对比exit函数),retval常常传NULL。

 这里为了打印效果家里互斥锁。(线程安全部分详细介绍)

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void *threadStart(void *args)
{std::string name = static_cast<char *>(args);int cnt = 0;while (true){sleep(1);pthread_mutex_lock(&mutex);std::cout << name << " is running..." << ", getpid(): " << getpid() << " , pthread_self():" << pthread_self() << std::endl;pthread_mutex_unlock(&mutex);if (cnt == 5){std::cout << name << " is stop" << std::endl;pthread_exit(nullptr);}cnt++;}return (void *)1;
}int main()
{pthread_t tid1;int n = pthread_create(&tid1, nullptr, threadStart, (void *)"thread-1");if (n != 0){std::cout << "pthread_create error" << std::endl;return 1;}while (true){sleep(1);pthread_mutex_lock(&mutex);std::cout << "main is running..." << std::endl;pthread_mutex_unlock(&mutex);}void *code = nullptr;n = pthread_join(tid1, &code);if (n == 0){std::cout << "main thread wait success, newthread exit code is " << (uint64_t)code << std::endl;}return 0;
}

 pthread_exit()与exit()不同,前者在线程中使用停止当前线程,而后者无论在哪里调用都会使整个程序停止运行。

int pthread_cancel(pthread_t thread)

  • 杀死指定的线程,成功返回0,失败返回错误号 。 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void *threadStart(void *args)
{std::string name = static_cast<char *>(args);while (true){sleep(1);pthread_mutex_lock(&mutex);std::cout << name << " is running..." << ", getpid(): " << getpid() << " , pthread_self():" << pthread_self() << std::endl;pthread_mutex_unlock(&mutex);}return (void *)1;
}int main()
{pthread_t tid1;int n = pthread_create(&tid1, nullptr, threadStart, (void *)"thread-1");if (n != 0){std::cout << "pthread_create error" << std::endl;return 1;}int cnt = 0;while (true){sleep(1);pthread_mutex_lock(&mutex);std::cout << "main is running..." << std::endl;pthread_mutex_unlock(&mutex);if(cnt == 5){pthread_cancel(tid1);std::cout << "-----------------------------" << std::endl;std::cout << "       pthread_cancel" << std::endl;std::cout << "-----------------------------" << std::endl;}cnt++;}void *code = nullptr;n = pthread_join(tid1, &code);if (n == 0){std::cout << "main thread wait success, newthread exit code is " << (uint64_t)code << std::endl;}return 0;
}

 

int pthread_detach(pthread_t thread)  

  • 实现线程分离成功返回0,失败返回错误号 。
  • 也可使用 pthread_create函数参2(线程属性)来设置线程分离。
  • 线程分离状态:线程主动与主线程断开关系。线程结束后,其退出状态不由其他线程获取,而直接自己自动释放。(分离后,不能使用pthread_join等待它的终止状态。)

线程安全

        线程安全指的是在多线程编程中,多个线程对临界资源进行争抢访问而不会造成数据二义或程序逻辑混乱的情况。

为什么要保证线程安全?

就以展示线程接口的代码为例,如果不加互斥锁限制主线程和1号线程对屏幕这份资源的争抢,就会出现如图这样的打印错乱。

 再举个例子如图,现在假设有cnt张票只要大于零就可以进行抢票。但如果不对多线程加以限制有可能会出现图中票被抢到负数的情况(不合理的情况)。

 

 临界资源和临界区

什么是临界资源和临界区?

  • 临界资源:一次只允许一个线程访问的共享资源。
  • 临界区:每个进程中访问临界资源的那一段代码。

 而锁的作用就是对临界区上锁,保证只有持有锁的那一个线程可以对临界资源进行访问。

互斥锁

同步与互斥
  • 互斥是指某一资源同时只允许一个访问者对其进行访问,但互斥无法控制对资源的访问顺序。
  • 同步是指在互斥的基础上实现对资源的有序访问。
 互斥锁的使用
锁的初始化

使用锁前,必须要先有一把锁,而锁有两种初始化方式。

动态方法:

调用pthread_mutex_init函数初始化,第一个参数传要初始化锁的指针,第二个参数是互斥量的属性一般传NULL使用缺省值。

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

当然第二个参数也可以不传NULL,还能有以下选择:

  •  PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。

  • PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。 

  • PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。

  • PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

静态方法:

pthread_mutex_t是一个结构体,而PTHREAD_MUTEX_INITIALIZER是一个结构体常量宏。

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int main()
{//静态:pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;//动态:pthread_mutex_t mutex2;pthread_mutex_init(&mutex2,nullptr);return 0;
}
加锁与解锁

只有拿到锁的线程才能去访问临界资源,pthread_mutex_lock()函数就是在竞争锁,抢到锁的线程去访问临界资源,没抢到的等待持有锁的线程进行解锁操作pthread_mutex_unlock()然后再进行竞争。(锁只能保证同一时间只有一个访问资源的线程,但无法控制线程访问资源的顺序,同一个线程连续抢到锁的情况也是有的)

 

  • int pthread_mutex_lock(pthread_mutex_t *mutex) 加锁,获取不到锁会挂起等待

  • int pthread_mutex_unlock(pthread_mutex_t *mutex)解锁

  • int pthread_mutex_trylock(pthread_mutex_t *mutex) 尝试获取锁锁被占据时返回EBUSY而不挂起等待

//线程要执行的函数
void *threadStart(void *args)
{std::string name = static_cast<char *>(args);while (true){sleep(1);//加锁pthread_mutex_lock(&mutex);std::cout << name << " is running..." << std::endl;//解锁pthread_mutex_unlock(&mutex);}return (void *)1;
}

  • int pthread_mutex_destroy(pthread_mutex_t *mutex) 销毁一个互斥(释放它所占用的资源),销毁时锁不能被占用。
int main()
{//静态:pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;//动态:pthread_mutex_t mutex2;pthread_mutex_init(&mutex2,nullptr);pthread_mutex_destroy(&mutex1);pthread_mutex_destroy(&mutex2);return 0;
}

信号量

        信号量是指可用资源的数量。

        具体些可以简单地理解为:当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。

信号与信号量的区别
  • 信号是Linux系统中用于进程之间通信或操作的一种机制,通常一种信号代表一种状态或一个操作。(冷知识:不处理本身也是一种操作)
  • 信号量是指可用资源的数量
 信号量的使用

Lock_guard与unique_lock

Lock_guard与unique_lock都是类,模板参数传入锁的类型,实例化时传入对应类型的锁作为构造函数的参数,用于上锁,生命周期到了会自动解锁。(初始化如下)

unique_lock<mutex> lock(mutex);
lock_guard<mutex> lock(mutex);

 原理:

简单实现了一下(仅供理解原理),原理也很简单,构造函数上锁,析构函数解锁,利用生命周期结束自动调用析构函数的特性解锁(即离开作用域)。

#pragma once
#include <iostream>
#include <pthread.h>class LockGuard
{
public://利用构造函数加锁LockGuard(pthread_mutex_t* mutex): _mutex(mutex){pthread_mutex_lock(_mutex);}//利用析构函数解锁~LockGuard(){pthread_mutex_unlock(_mutex);}private:pthread_mutex_t* _mutex;
};

使用方法:

//线程要执行的函数
void *threadStart(void *args)
{std::string name = static_cast<char *>(args);while (true){sleep(1);       //加锁unique_lock<mutex> lock(mutex);//使用lock_guard<mutex> lock(mutex)替换也可std::cout << name << " is running..." << std::endl;}return (void *)1;
}

除了正常使用,还可以像这样用大括号限定出一个作用域

int main()
{pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;{unique_lock<mutex> lock(mutex);}{lock_guard<mutex> lock(mutex);}return 0;
}

线程池

原理与作用

原理:

        线程池通过一个线程安全的阻塞任务队列加上一个或一个以上的线程实现,线程池中的线程可以从阻塞队列中获取任务进行任务处理,当线程都处于繁忙状态时可以将任务加入阻塞队列中,等到其它的线程空闲后进行处理。

作用:

   可以避免大量线程频繁创建或销毁所带来的时间成本,也可以避免在峰值压力下,系统资源耗尽的风险;并且可以统一对线程池中的线程进行管理,调度监控。

完整代码

线程封装代码:

#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>namespace ThreadMoudle
{class Thread{public:using func_t = std::function<void(std::string)>;Thread(const std::string &name, const func_t &func): _name(name), _func(func){}void Excute(){_isrunning = true;_func(_name);_isrunning = false;}static void *ThreadRoutine(void *args){Thread *self = static_cast<Thread *>(args);self->Excute();return nullptr;}bool start(){int n = pthread_create(&_tid, nullptr, ThreadRoutine, this);if (n != 0)return false;return true;}void stop(){if (_isrunning){pthread_cancel(_tid);_isrunning = false;}}void join(){pthread_join(_tid, nullptr);}~Thread(){}private:pthread_t _tid;std::string _name;bool _isrunning;func_t _func;};
};

日志代码:

#pragma once
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <ctime>
#include <cstdarg>
#include <fstream>
#include <cstring>
#include <pthread.h>
#include "LockGuard.hpp"namespace log_ns
{#define SCREEN_TYPE 1
#define FILE_TYPE 2enum{DEBUG = 1,INFO,WARNING,ERROR,FATAL};class LogMessage{public:std::string _level;pid_t _id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;};const std::string logfile = "./log.txt";pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;class log{private:void FlushLogToScreen(LogMessage &log){printf("[%s][%d][%s][%d][%s] %s",log._level.c_str(),log._id,log._filename.c_str(),log._filenumber,log._curr_time.c_str(),log._message_info.c_str());}void FlushLogToFile(LogMessage &log){std::ofstream out(logfile, std::ios::app);char logtxt[1024];snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%d][%s] %s",log._level.c_str(),log._id,log._filename.c_str(),log._filenumber,log._curr_time.c_str(),log._message_info.c_str());out.write(logtxt, strlen(logtxt));out.close();}void FlushLog(LogMessage &log){switch (_type){case SCREEN_TYPE:FlushLogToScreen(log);break;case FILE_TYPE:FlushLogToFile(log);break;}}std::string LevelToString(int level){switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "UNKNOW";}}std::string GetCurTime(){time_t now = time(nullptr);struct tm *curtime = localtime(&now);char buffer[1024];snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",curtime->tm_year + 1900,curtime->tm_mon + 1,curtime->tm_mday,curtime->tm_hour,curtime->tm_min,curtime->tm_sec);return buffer;}public:log(const std::string &filename = logfile) : _filename(filename), _type(SCREEN_TYPE){}~log(){}void Enable(int type){_type = type;}void logMessage(const std::string &filename, int filenumber, int level, const char *format, ...){LogMessage log;log._filename = filename;log._filenumber = filenumber;log._id = getpid();log._level = LevelToString(level);log._curr_time = GetCurTime();va_list ap;va_start(ap, format);char info[1034];vsnprintf(info, sizeof(info), format, ap);va_end(ap);log._message_info = info;FlushLog(log);}private:int _type;std::string _filename;};log_ns::log lg;#define LOG(Level, Format, ...)                                          \do                                                                   \{                                                                    \lg.logMessage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \} while (0)
#define EnableScreen()          \do                          \{                           \lg.Enable(SCREEN_TYPE); \} while (0)
#define EnableFILE()          \do                        \{                         \lg.Enable(FILE_TYPE); \} while (0)
};

线程池完整代码:

#pragma once
#include <iostream>
#include <queue>
#include <string>
#include <vector>
#include <functional>
#include <pthread.h>
#include "Thread.hpp"
#include "Log.hpp"using namespace log_ns;static const int gdefualtnum = 5;template <class T>
class ThreadPool
{
private:void LockQueue(){pthread_mutex_lock(&_mutex);}void UnLockQueue(){pthread_mutex_unlock(&_mutex);}void WakeUp(){pthread_cond_signal(&_cond);}void WakeUpAll(){pthread_cond_broadcast(&_cond);}void Sleep(){pthread_cond_wait(&_cond, &_mutex);}bool TQIsEmpty(){return _task_queue.empty();}void HandlerTask(std::string name){while (true){LockQueue();while (TQIsEmpty() && _isrunning){_sleep_thread_num++;Sleep();_sleep_thread_num--;}if (TQIsEmpty() && !_isrunning){LOG(DEBUG, "%s : quit\n", name.c_str());UnLockQueue();break;}T task = _task_queue.front();_task_queue.pop();LOG(DEBUG, "hander task done, task is : %s\n", task.result().c_str());UnLockQueue();task();}}ThreadPool(int thread_num = gdefualtnum) : _thread_num(thread_num), _isrunning(false), _sleep_thread_num(0){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);}ThreadPool(const ThreadPool<T> &) = delete;void operator=(const ThreadPool<T> &) = delete;public:~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}void Init(){ThreadMoudle::Thread::func_t func = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);for (int i = 0; i < _thread_num; i++){std::string threadname = "pthread_" + std::to_string(i + 1);_threads.emplace_back(threadname, func);}}void Start(){_isrunning = true;for (auto &thread : _threads){thread.start();}}void Stop(){LockQueue();_isrunning = false;if (_sleep_thread_num > 0)WakeUpAll();UnLockQueue();}void Equeue(const T &in){LockQueue();if (_isrunning){_task_queue.push(in);if (_sleep_thread_num > 0)WakeUp();}UnLockQueue();}static ThreadPool<T> *GetInstance(){if (_tp == nullptr){LockGuard lockguard(&_sig_mutex);if (_tp == nullptr){LOG(INFO, "create threadpool\n");_tp = new ThreadPool();_tp->Init();_tp->Start();}else{LOG(INFO, "get threadpool\n");}}return _tp;}private:int _thread_num;int _sleep_thread_num;bool _isrunning;std::vector<ThreadMoudle::Thread> _threads;std::queue<T> _task_queue;pthread_mutex_t _mutex;pthread_cond_t _cond;static ThreadPool<T> *_tp;static pthread_mutex_t _sig_mutex;
};template <typename T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;
template <typename T>
pthread_mutex_t ThreadPool<T>::_sig_mutex = PTHREAD_MUTEX_INITIALIZER;

相关文章:

【Linux】多线程

目录 线程 线程和进程的关系 虚拟地址与物理地址的映射&#xff08;三级映射&#xff09; ​编辑 线程的使用 线程安全 临界资源和临界区 互斥锁 同步与互斥 互斥锁的使用 锁的初始化 加锁与解锁 信号量 信号与信号量的区别 信号量的使用 Lock_guard与unique_lock …...

如何使用Python库连接Redis

1、redis-py 库封装一个 Redis 工具类可以帮助我们简化 Redis 的操作并提高代码的复用性和可维护性。 安装redis pip install redisimport redis import logginglogging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__)class RedisUtils:def __init__(s…...

mybatis一对多的查询怎么查?

1.问题描述 我有一个班级&#xff0c;班级里有很多学生&#xff0c;我需要一个查询把班级和学生信息查出来&#xff0c;怎么查&#xff1f; 这里有个问题&#xff0c;就是我班级的命名和学生的命名可能重了。 例如&#xff1a; class Gradle{ private String id; private …...

[C++]友元函数和友元类

1. 友元函数 1.1 友元函数的概念 友元函数是一个非成员函数&#xff0c;它被类声明为“友元”&#xff0c;从而能够访问类的私有成员和保护成员。正常情况下&#xff0c;类的外部函数是不能访问类的私有成员的&#xff0c;而友元函数打破了这一限制。 class MyClass { priva…...

实战:MyBatis适配多种数据库:MySQL、Oracle、PostGresql等

概叙 很多时候&#xff0c;一套代码要适配多种数据库&#xff0c;主流的三种库&#xff1a;MySQL、Oracle、PostGresql&#xff0c;刚好mybatis支持这种扩展&#xff0c;如下图所示&#xff0c;在一个“namespace”&#xff0c;判断唯一的标志是iddatabaseId&#xff0c;刚好写…...

2024年天津市职业院校技能大赛高职组 “信息安全管理与评估”样题第三阶段

&#xff08;四&#xff09;第三阶段竞小组&#xff08;赛项&#xff09;目&#xff08;300分&#xff09; 第三阶段竞赛内容是:网络安全渗透&#xff08;夺旗挑战赛CTF&#xff09; 本模块要求参赛者作为攻击方&#xff0c;运用所学的信息收集、漏洞发现、漏洞利用等渗透测试技…...

游戏引擎学习第36天

仓库 :https://gitee.com/mrxiao_com/2d_game 回顾之前的内容 在这个程序中&#xff0c;目标是通过手动编写代码来从头开始制作一个完整的游戏。整个过程不使用任何库或现成的游戏引擎&#xff0c;这样做的目的是为了能够全面了解游戏执行的每一个细节。开发过程中&#xff0…...

数仓技术hive与oracle对比(一)

准备 包括软硬件环境、数据、测试数据三方面的准备内容。 环境 虚拟机软件virtualbox7&#xff0c;同样的虚拟机配置&#xff1a;内存2G、cpu一核&#xff0c;物理主机同一台macbookpro&#xff08;13-2020款&#xff09;&#xff0c;所以硬盘IO读写速度一致。 综上&#x…...

LeetCode题集-5 - 最长回文子串(一)

题目&#xff1a;给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 这一题作为中等难度&#xff0c;常规解法对于大多数人应该都没有难度。但是其中也有超难的解决办法&#xff0c;下面我们就一起由易到难&#xff0c;循序渐进地来解这道题。 01、暴力破解法 对于大多…...

A3026 Java+jsp+servlet+mysql高校学生请假管理系统

高校学生请假管理系统 1.摘要2. 绪论3.功能结构4.界面展示5.源码获取 1.摘要 高校学生请假管理系统 摘要&#xff1a;随着计算机的发展与不断进步&#xff0c;各个领域都出现了新的技术&#xff0c;曾经各种规模之间的竞争已经发展成为技术之间的竞争&#xff0c;管理和人才之…...

LDO低压差线性稳压器

1. 简介 LDO 是 “Low Dropout Regulator” 的缩写&#xff0c;中文称为“低压差线性稳压器”。LDO 稳压器是一种用于电压调节的电子设备&#xff0c;它的主要特点是输出电压和输入电压之间的压差非常低。这种特性使得 LDO 在许多应用场景中非常有用&#xff0c;特别是在需要高…...

Angular由一个bug说起之十一:排序之后无法展开 Row

问题现象 在使用 Material Table 时&#xff0c;排序功能触发了一个奇怪的 Bug&#xff1a;表格的 Row 无法展开。最终排查发现&#xff0c;问题的根源在于 trackBy 的错误使用。trackBy 方法接受两个参数&#xff1a;index&#xff08;数据索引&#xff09;和 row&#xff08;…...

wlanapi.dll丢失怎么办?有没有什么靠谱的修复wlanapi.dll方法

在遇到各种系统文件错误当中&#xff0c;其中之一就是“wlanapi.dll文件丢失”的问题。这种问题通常发生在Windows操作系统上&#xff0c;特别是当系统试图执行与无线网络相关的任务时。wlanapi.dll是一个重要的系统文件&#xff0c;它负责处理Windows无线网络服务的许多功能。…...

redis安装和使用教程【保姆级】

1.下载 通过网盘分享的文件&#xff1a;redis 链接: https://pan.baidu.com/s/1Tu1KZkf33YJFdul8s6SzqQ?pwd8888 提取码: 8888 2.启动 进入根目录&#xff0c;使用redis-server redis.windows.conf命令启行启动Redis服务&#xff0c; 如下图所示为启动成功&#xff0c;默认…...

Github 2024-12-01 开源项目月报 Top20

根据Github Trendings的统计,本月(2024-12-01统计)共有20个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目10TypeScript项目9Go项目2HTML项目1Shell项目1Jupyter Notebook项目1屏幕截图转代码应用 创建周期:114 天开发语言:TypeScript, Py…...

C总结(C语言知识点,深化重难点)

C语言 1.使用C语言的7个步骤2.ASCII码3.提高程序可读性的机巧4.如何使用多种整形5.打印多种整形6.课移植类型&#xff1a;stdint.h和inttypes.h7.浮点数常量8.浮点值的上溢和下溢9.使用数据类型11.常量和C预处理器12.转换说明的意义12.1转换不匹配13.副作用和序列点14.数组简介…...

[Collection与数据结构] 位图与布隆过滤器

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…...

Redis与缓存

目录 缓存 缓存优缺点 缓存更新策略 超时剔除 先删缓存再更新数据库 旁路缓存(先更新数据库&#xff0c;再删缓存&#xff09; 先更新数据库&#xff0c;再更新缓存 读写穿透 ​编辑 异步缓存写入模式 缓存常见问题 缓存穿透 缓存雪崩 缓存击穿 缓存 在业务开发…...

Ubuntu Linux 文件、目录权限问题(五)

本文为Ubuntu Linux操作系统- 第五弹 此文是在上期文件目录的内容操作基础上接着讲权限问题 上期回顾&#xff1a;Ubuntu Linux 目录和文件的内容操作 文件访问者身份与文件访问权限 Linux文件结构 所有者&#xff08;属主&#xff09;所属组&#xff08;属组&#xff09;其他…...

AI 名人堂:Jeff Dean

Jeff Dean&#xff0c;谷歌的高级研究员和人工智能领域的领军人物&#xff0c;以其在大规模分布式计算系统和人工智能系统的杰出贡献而闻名。 谷歌AI掌门人 TensorFlow项目负责人 美国工程院院士 2AGI.NET AI 名人堂 AI 名人堂&#xff1a;Jeff DeanAI 名人堂&#xff1a;Je…...

基础排序算法详解:冒泡排序、选择排序与插入排序

引言 上一章&#xff0c;我们聊到了排序的基本概念和常见算法的分类。这一次&#xff0c;我们从基础开始&#xff0c;深入剖析三种常见的O(n) 排序算法&#xff1a;冒泡排序、选择排序 和 插入排序。 它们是学习排序算法的入门神器&#xff0c;不仅实现简单&#xff0c;还能帮…...

Flink如何基于数据版本使用最新离线数据

业务场景 假设批量有一张商户表&#xff0c;表字段中有商户名称和商户分类两个字段。 批量需要将最新的商户名称和分类的映射关系推到hbase供实时使用。 原实现方案 a.原方案内容 为解决批量晚批问题&#xff0c;批量推送hbase表时一份数据产生两类rowkey&#xff1a;T-1和…...

什么是反向代理?作用、原理和实例详解

&#x1f680; 什么是反向代理&#xff1f;作用、原理和实例详解 在现代的网络架构中&#xff0c;反向代理&#xff08;Reverse Proxy&#xff09;无处不在。无论是负载均衡、加速缓存&#xff0c;还是WebSocket 支持&#xff0c;反向代理都是必不可少的工具。 这篇文章将带您…...

国产GPU中,VLLM0.5.0发布Qwen2.5-14B-Instruct-GPTQ-Int8模型,请求返回结果乱码

概述 国产GPU: DCU Z100 推理框架&#xff1a; vllm0.5.0 docker容器化部署 运行如下代码&#xff1a; python -m vllm.entrypoints.openai.api_server --model /app/models/Qwen2.5-14B-Instruct-GPTQ-Int8 --served-model-name qwen-gptq --trust-remote-code --enforce…...

Stable Diffusion本地部署:从零开始的完整指南

1、引言 Stable Diffusion是计算机视觉领域的一个生成式大模型&#xff0c;能够进行文生图&#xff08;txt2img&#xff09;和图生图&#xff08;img2img&#xff09;等图像生成任务。它利用深度学习技术&#xff0c;特别是RealisticVision v2.0模型&#xff0c;能够创造出接近…...

隐式神经网络实现低光照图像增强

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…...

Flutter动画(三)内建显式动画Widget

常见的内建显式动画Widget&#xff1a; ListenableBuilder&#xff1a; AnimatedBuilder AnimatedWidget AlignTransition DecoratedBoxTransition DefaultTextStyleTransition PositionedTransition RelativePositionedTransition RotationTransition ScaleTransiti…...

springSecurity自定义登陆接口和JWT认证过滤器

下面我会根据该流程图去自定义接口&#xff1a; 我们需要做的任务有&#xff1a; 登陆&#xff1a;1、通过ProviderManager的方法进行认证&#xff0c;生成jwt&#xff1b;2、把用户信息存入redis&#xff1b;3、自定义UserDetailsService实现到数据库查询数据的方法。 校验&a…...

Spring Boot日志:从Logger到@Slf4j的探秘

写在前面 Hello大家好&#xff0c;今日是2024年的第一天&#xff0c;祝大家元旦快乐?? 2024第一篇文章从SpringBoot日志开始 文章目录 一、前言二、日志有什么用&#xff1f;三、日志怎么用&#xff1f;四、自定义日志打印 ?? 常见日志框架说明4.1 在程序中得到?志对象【…...

使用 LabVIEW 与 PLC 通信的方式

要将 PLC 与 LabVIEW 或其他 NI 产品进行通信&#xff0c;首先需要明确 PLC 支持的通信协议和接口类型。NI 提供了多种方案&#xff0c;包括 OPC 服务器、Modbus、Ethernet/IP 和其他工业通信协议。下面将详细介绍这些方法&#xff0c;并进行比较分析&#xff0c;帮助你选择最适…...

python录制鼠标键盘操作循环播放

依赖 pip install pynput 程序: from pynput import mouse, keyboard import time import threading# 用于存储录制的鼠标和键盘事件 mouse_events [] keyboard_events []# 定义事件处理函数# 处理鼠标事件 def on_move(x, y):mouse_events.append((move, x, y))def on_cl…...

开发者如何使用GCC提升开发效率Opencv操作

看此篇前请先阅读 https://blog.csdn.net/qq_20330595/article/details/144134160?spm=1001.2014.3001.5502 https://blog.csdn.net/qq_20330595/article/details/144134160?spm=1001.2014.3001.5502 https://blog.csdn.net/qq_20330595/article/details/144216351?spm=1001…...

异常与文件

目录 1.异常 1.1.概念 1.2.常见异常 1.3.异常处理方式 1.3.1.try except 1.3.2.try except else 1.3.3.try except else finally 2.文件 2.1.文件分类 ps&#xff1a;python 程序的数据保存在哪里? 2.2.常见的文件类型 2.3.python 操作文件的函数 2.3.1.读取文件…...

【C语言】完成程序设计填空

文章目录 1、请阅读下面的程序,在空白处填写正确的代码,要求各在一行从头开始输出m和n的值。2、求100~599之间的所有水仙花数,即各位数字的立方和恰好等于该数本身的数。3、以下程序的功能是:将值为三位正整数的变量x中的数值按照个位、十位、百位的顺序 拆分并输出。请填空…...

西湖大学:LLM零样本推理任务校准

&#x1f4d6;标题&#xff1a;Task Calibration: Calibrating Large Language Models on Inference Tasks &#x1f310;来源&#xff1a;arXiv, 2410.18764 &#x1f31f;摘要 &#x1f538;大型语言模型&#xff08;LLM&#xff09;在推理任务上表现出令人印象深刻的零样本…...

windows下Qt5自动编译配置QtMqtt环境(11)

文章目录 [toc]1、概述2、准备1.1 下载源码1.2 配置环境1.3 解释原理 3、编译4、验证5、参考6、视频 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;Qt网络编程 &#x1f448; 1、概述 Qt默认是不包含mqtt库的&#xff0c;如果需要使用到mqtt库就只能自己编译配…...

每天五分钟深度学习:神经网络的前向传播的计算(多样本)

本文重点 前面我们学习了单样本的前向传播,本文我们学习多样本的前向传播,我们先来回忆一下,神经网络的单样本的前向传播的向量化的方式: m个样本依次进行前向传播 这里我们说明一下符号: 我们使用(m)表示第m个样本,用[m]表示神经网络的第m层 a[2](i) 表示第i个样本计…...

基于 NXP S32K312+FS23 的汽车通用评估板方案

S32K3 系列是 NXP 推出的面向汽车电子和工业应用的微控制器&#xff0c;基于 ARMCortex-M7 内核&#xff0c;支持单核、双核和锁步内核配置。S32K3 系列具有内核、内存和外设数量方面的可扩展性&#xff0c;符合 ISO26262 标准&#xff0c;能达到 ASIL B/D 安全等级&#xff0c…...

11进阶篇:专业课论文阅读方向指南(2025版)

文章目录 第一个检索式:图情档核心期刊(北大 + CSSCI)发文情况研究方法类关键词研究主题类关键词论文阅读建议第二个检索式:川大公共管理学院在核心期刊(北大 + CSSCI)的发文情况研究方法类关键词研究主题类关键词特点关键词与2024年972(现815)两道题目的映射情况815信…...

Qt之第三方库‌QXlsx使用(三)

Qt开发 系列文章 - QXlsx&#xff08;三&#xff09; 目录 前言 一、Qt开源库 二、QXlsx 1.QXlsx介绍 2.QXlsx下载 3.QXlsx移植 4.修改项目文件.pro 三、使用技巧 1.写入数据 2.读出数据 总结 前言 Qt第三方控件库是指非Qt官方提供的、用于扩展Qt应用程序功能的控件…...

第145场双周赛: 使数组的值全部为 K 的最少操作次数、破解锁的最少时间 Ⅰ、使两个整数相等的位数操作、统计最小公倍数图中的连通块数目

Q1、使数组的值全部为 K 的最少操作次数 1、题目描述 给你一个整数数组 nums 和一个整数 k 。 如果一个数组中所有 严格大于 h 的整数值都 相等 &#xff0c;那么我们称整数 h 是 合法的 。 比方说&#xff0c;如果 nums [10, 8, 10, 8] &#xff0c;那么 h 9 是一个 合法…...

AJAX三、XHR,基本使用,查询参数,数据提交,promise的三种状态,封装-简易axios-获取省份列表 / 获取地区列表 / 注册用户,天气预报

一、XMLHttpRequest基本使用 XMLHttpRequest&#xff08;XHR&#xff09;对象用于与服务器交互。 二、XMLHttpRequest-查询参数 语法: 用 & 符号分隔的键/值对列表 三、XMLHttpRequest-数据提交 核心步骤 : 1. 请求头 设置 Content-Type 2. 请求体 携带 符合要求 的数…...

Android期末复习题

1.如何搭建Android开发环境&#xff1f; 答案:搭建Android开发环境需要以下几个步骤&#xff1a; &#xff08;1&#xff09;下载和安装JDK &#xff08;2&#xff09;配置PATH环境变量 &#xff08;3&#xff09;下载和安装Android Studio &#xff08;4&#xff09;创建A…...

《蓝桥杯比赛规划》

一、比赛简介 蓝桥杯全国软件和信息技术专业人才大赛是一项具有较高影响力的编程竞赛&#xff0c;旨在促进软件和信息技术领域专业技术人才的培养&#xff0c;提升高校毕业生的就业竞争力。比赛涵盖了多个编程语言和专业方向&#xff0c;包括 C/C、Java、Python 等。 二、目标…...

三、Zookeeper

Zookeeper 三、Zookeeper3.1什么是zookeeper?3.2为什么需要zookeeper3.3Zookeeper基本运行流程3.4Zookeeper数据模型3.5Zookeeper主要角色3.6Zookeeper工作原理3.7Zookeeper节点数据操作流程三、Zookeeper 3.1什么是zookeeper? ZooKeeper是一个分布式的,开放源码的分布式应…...

Wireshark数据抓包分析之传输层协议(TCP协议)

根据实验环境&#xff0c;本实验的步骤如下&#xff1a; 1.在测试环境使用发包工具和Wireshark抓取TCP三次握手和四次断开的数据包。 2.详细分析TCP协议的三次握手以及四次断开。 任务描述&#xff1a;安装发包工具&#xff0c;并配置TCP客户端&#xff0c;服务端&#xff0…...

用ai做机器视觉的事情

cnn&#xff08;卷积神经网络&#xff09;是典型的ai算法。 我们已经cnn实现像机器视觉中形状匹配的功能&#xff0c;因为使用了roi抠图匹配&#xff0c;所以就叫做roicnn&#xff0c;以区分整图匹配。下面是roicnn笔记总结&#xff1a; 20241022&#xff0c;roicnn搞定&…...

LLM - 开源视觉多模态 LLaVA-CoT(o1) 深度推理模型 测试与源码 教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/144304351 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 LLaVA-…...

qtcanpool 知 10:包管理雏形

文章目录 前言痛点转机雏形实践后语 前言 曾听闻&#xff1a;C/Qt 没有包管理器&#xff0c;开发起来太不方便。这是一个有过 node.js 开发经验的人对 Qt 的吐槽。 确实&#xff0c;像 python、golang、node.js 这些编程语言都有包管理器&#xff0c;给用户带来了极佳的开发体…...

[保姆式教程]使用目标检测模型YOLO11 OBB进行旋转目标检测:训练自己的数据集(基于卫星和无人机的农业大棚数据集)

之前写了一个基于YOLOv8做旋转目标检测&#xff08;OBB&#xff09;的文章&#xff0c;内容写得不够好&#xff0c;内容也有些杂乱无序。现如今YOLO已经更新到11了&#xff0c;数据集也集齐了无人机和卫星的农业大棚&#xff0c;所以这次就写一个基于YOLO11 OBB的农业大棚旋转检…...