最新资讯

  • 【在Linux世界中追寻伟大的One Piece】多线程(三)

【在Linux世界中追寻伟大的One Piece】多线程(三)

2025-04-27 06:01:27 0 阅读

目录

1 -> Linux线程同步

1.1 -> 条件变量

1.2 -> 同步概念与竞态条件

1.3 -> 条件变量函数

1.4 -> 为什么pthread_cond_wait需要互斥量

1.5 -> 条件变量使用规范

2 -> 生产者消费者模型

2.1 -> 为什么要使用生产者消费者模型

2.2 -> 生产者消费者模型优点

2.3 -> 基于BlockingQueue的生产者消费者模型

2.4 -> C++ queue模拟阻塞队列的生产消费模型

2.5 -> POSIX信号量

2.6 -> 基于环形队列的生产消费模型

3 -> 线程池

3.1 -> 线程池概念

3.2 -> 线程池应用场景

3.3 -> 线程池示例

4 -> 线程安全的单例模式

4.1 -> 什么是单例模式

4.2 -> 单例模式的特点

4.3 -> 饿汉方式实现单例模式

4.4 -> 懒汉方式实现单例模式

4.5 -> 懒汉方式实现单例模式(线程安全版本)

5 -> STL,智能指针和线程安全

5.1 -> STL中的容器是否是线程安全的

5.2 -> 智能指针是否是线程安全的


1 -> Linux线程同步

1.1 -> 条件变量

  • 当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。
  • 例如一个线程访问队列时,发现队列为空,它只能等待,直到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。

1.2 -> 同步概念与竞态条件

  • 同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步。
  • 竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。在线程场景下,这种问题不难理解。

1.3 -> 条件变量函数

初始化

int pthread_cond_init(pthread_cond_t* restrict cond, const pthread_condattr_t* restrict attr);

参数:
cond:要初始化的条件变量
attr:NULL

销毁

int pthread_cond_destroy(pthread_cond_t* cond);

等待条件满足

int pthread_cond_wait(pthread_cond_t* restrict cond, pthread_mutex_t* restrict mutex);

参数:
cond:要在这个条件变量上等待
mutex:互斥量

唤醒等待

int pthread_cond_broadcast(pthread_cond_t* cond);
int pthread_cond_signal(pthread_cond_t* cond);

简单案例

#include 
#include 
#include 
#include 
#include 

pthread_cond_t cond;
pthread_mutex_t mutex;

void* r1(void* arg)
{
	while (1) 
	{
		pthread_cond_wait(&cond, &mutex);
		printf("活动
");
	}
}

void* r2(void* arg)
{
	while (1) 
	{
		pthread_cond_signal(&cond);
		sleep(1);
	}
}

int main(void)
{
	pthread_t t1, t2;

	pthread_cond_init(&cond, NULL);
	pthread_mutex_init(&mutex, NULL);

	pthread_create(&t1, NULL, r1, NULL);
	pthread_create(&t2, NULL, r2, NULL);

	pthread_join(t1, NULL);
	pthread_join(t2, NULL);

	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);
}
[root@localhost linux]# ./a.out
活动
活动
活动

1.4 -> 为什么pthread_cond_wait需要互斥量

  • 条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足,所以必须要有一个线程通过某些操作,改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程。
  • 条件不会无缘无故的突然变得满足,必然会牵扯到共享数据的变化。所以一定要用互斥锁来保护。没有互斥锁就无法安全的获取和修改共享数据。

  • 按照上面的说法,设计出如下的代码:先上锁,发现条件不满足,解锁,然后等待在条件变量上就行了。
// 错误的设计
pthread_mutex_lock(&mutex);
while (condition_is_false) 
{
	pthread_mutex_unlock(&mutex);
	//解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过
	pthread_cond_wait(&cond);
	pthread_mutex_lock(&mutex);

}
pthread_mutex_unlock(&mutex);
  • 由于解锁和等待不是原子操作。调用解锁之后,pthread_cond_wait之前,如果已经有其他线程获取到互斥量,摒弃条件满足,发送了信号,那么pthread_cond_wait将错过这个信号,可能会导致线程永远阻塞在这个pthread_cond_wait。所以解锁和等待必须是一个原子操作。
  • int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);进入该函数后,会去看条件量是否等于0,等于0,就把互斥量变成1,直到cond_wait返回,把条件量改成1,把互斥量恢复成原样。

1.5 -> 条件变量使用规范

  • 等待条件代码

pthread_mutex_lock(&mutex);
while (条件为假)
	pthread_cond_wait(cond, mutex);
修改条件
pthread_mutex_unlock(&mutex);
  • 给条件发送信号代码
pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);

2 -> 生产者消费者模型

2.1 -> 为什么要使用生产者消费者模型

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者索要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。

2.2 -> 生产者消费者模型优点

  • 解耦
  • 支持并发
  • 支持忙闲不均

2.3 -> 基于BlockingQueue的生产者消费者模型

在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于:当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队列进程操作时会被阻塞)。

2.4 -> C++ queue模拟阻塞队列的生产消费模型

代码:

#include 
#include 
#include 
#include 

#define NUM 8

class BlockQueue 
{
private:
	std::queue q;
	int cap;
	pthread_mutex_t lock;
	pthread_cond_t full;
	pthread_cond_t empty;

private:
	void LockQueue()
	{
		pthread_mutex_lock(&lock);
	}

	void UnLockQueue()
	{
		pthread_mutex_unlock(&lock);
	}

	void ProductWait()
	{
		pthread_cond_wait(&full, &lock);
	}

	void ConsumeWait()
	{
		pthread_cond_wait(&empty, &lock);
	}

	void NotifyProduct()
	{
		pthread_cond_signal(&full);
	}

	void NotifyConsume()
	{
		pthread_cond_signal(&empty);
	}

	bool IsEmpty()
	{
		return (q.size() == 0 ? true : false);
	}

	bool IsFull()
	{
		return (q.size() == cap ? true : false);
	}

public:
	BlockQueue(int _cap = NUM) :cap(_cap)
	{
		pthread_mutex_init(&lock, NULL);
		pthread_cond_init(&full, NULL);
		pthread_cond_init(&empty, NULL);
	}

	void PushData(const int& data)
	{
		LockQueue();
		while (IsFull()) 
		{
			NotifyConsume();
			std::cout << "queue full, notify
				consume data, product stop." << std::endl;
				ProductWait();
		}

		q.push(data);
		// NotifyConsume();
		UnLockQueue();
	}

	void PopData(int& data)
	{
		LockQueue();
		while (IsEmpty()) 
		{
			NotifyProduct();
			std::cout << "queue empty, notify
				product data, consume stop." << std::endl;
				ConsumeWait();
		}

		data = q.front();
		q.pop();
		// NotifyProduct();
		UnLockQueue();
	}

	~BlockQueue()
	{
		pthread_mutex_destroy(&lock);
		pthread_cond_destroy(&full);
		pthread_cond_destroy(&empty);
	}
};

void* consumer(void* arg)
{
	BlockQueue* bqp = (BlockQueue*)arg;
	int data;
	for (; ; ) 
	{
		bqp->PopData(data);
		std::cout << "Consume data done : " << data <<
			std::endl;
	}
}

//more faster
void* producter(void* arg)
{
	BlockQueue* bqp = (BlockQueue*)arg;
	srand((unsigned long)time(NULL));
	for (; ; ) 
	{
		int data = rand() % 1024;
		bqp->PushData(data);
		std::cout << "Prodoct data done: " << data <<
			std::endl;
		// sleep(1);
	}
}

int main()
{
	BlockQueue bq;

	pthread_t c, p;

	pthread_create(&c, NULL, consumer, (void*)&bq);
	pthread_create(&p, NULL, producter, (void*)&bq);

	pthread_join(c, NULL);
	pthread_join(p, NULL);

	return 0;
}

2.5 -> POSIX信号量

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。但POSIX可以用于线程间同步。

初始化信号量

#include 
int sem_init(sem_t* sem, int pshared, unsigned int value);

参数:
pshared : 0 表示线程间共享,非零表示进程间共享
value:信号量初始值

销毁信号量

int sem_destroy(sem_t* sem);

等待信号量

功能:等待信号量,会将信号量的值减 1
int sem_wait(sem_t * sem); //P()

发布信号量

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加 1。
int sem_post(sem_t * sem);//V()

生产者-消费者的例子是基于queue的,其空间可以动态分配,现在基于固定大小的环形队列重写这个程序(POSIX信号量)。

2.6 -> 基于环形队列的生产消费模型

  • 环形队列采用数组模拟,用模运算来模拟环状特性。

  • 环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态。

  • 但是现有的信号量这个计数器,是简单的进行多进程间的同步过程。
#include 
#include 
#include 
#include 
#include 

#define NUM 16

class RingQueue 
{
private:
	std::vector q;
	int cap;
	sem_t data_sem;
	sem_t space_sem;
	int consume_step;
	int product_step;

public:
	RingQueue(int _cap = NUM) :q(_cap), cap(_cap)
	{
		sem_init(&data_sem, 0, 0);
		sem_init(&space_sem, 0, cap);
		consume_step = 0;
		product_step = 0;
	}

	void PutData(const int& data)
	{
		sem_wait(&space_sem); // P
		q[consume_step] = data;
		consume_step++;
		consume_step %= cap;
		sem_post(&data_sem); //V
	}

	void GetData(int& data)
	{
		sem_wait(&data_sem);
		data = q[product_step];
		product_step++;
		product_step %= cap;
		sem_post(&space_sem);
	}

	~RingQueue()
	{
		sem_destroy(&data_sem);
		sem_destroy(&space_sem);
	}
};

void* consumer(void* arg)
{
	RingQueue* rqp = (RingQueue*)arg;
	int data;
	for (; ; ) 
	{
		rqp->GetData(data);
		std::cout << "Consume data done : " << data << std::endl;
		sleep(1);
	}
}

//more faster
void* producter(void* arg)
{
	RingQueue* rqp = (RingQueue*)arg;
	srand((unsigned long)time(NULL));
	for (; ; ) 
	{
		int data = rand() % 1024;
		rqp->PutData(data);
		std::cout << "Prodoct data done: " << data << std::endl;
		// sleep(1);
	}
}

int main()
{
	RingQueue rq;

	pthread_t c, p;

	pthread_create(&c, NULL, consumer, (void*)&rq);
	pthread_create(&p, NULL, producter, (void*)&rq);

	pthread_join(c, NULL);
	pthread_join(p, NULL);

}

3 -> 线程池

3.1 -> 线程池概念

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络 sockets 等的数量。

3.2 -> 线程池应用场景

  1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
  2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
  3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

3.3 -> 线程池示例

threadpool.hpp

#ifndef __M_TP_H__
#define __M_TP_H__

#include 
#include 
#include 

#define MAX_THREAD 5

typedef bool (*handler_t)(int);

class ThreadTask
{
private:
	int _data;
	handler_t _handler;
public:
	ThreadTask() :_data(-1), _handler(NULL) {}

	ThreadTask(int data, handler_t handler) 
	{
		_data = data;
		_handler = handler;
	}

	void SetTask(int data, handler_t handler) 
	{
		_data = data;
		_handler = handler;
	}

	void Run() 
	{
		_handler(_data);
	}
};

class ThreadPool
{
private:
	int _thread_max;
	int _thread_cur;
	bool _tp_quit;
	std::queue _task_queue;
	pthread_mutex_t _lock;
	pthread_cond_t _cond;

private:
	void LockQueue() 
	{
		pthread_mutex_lock(&_lock);
	}

	void UnLockQueue() 
	{
		pthread_mutex_unlock(&_lock);
	}

	void WakeUpOne() 
	{
		pthread_cond_signal(&_cond);
	}

	void WakeUpAll() 
	{
		pthread_cond_broadcast(&_cond);
	}

	void ThreadQuit() 
	{
		_thread_cur--;
		UnLockQueue();
		pthread_exit(NULL);
	}

	void ThreadWait() 
	{
		if (_tp_quit) 
		{
			ThreadQuit();
		}
		pthread_cond_wait(&_cond, &_lock);
	}

	bool IsEmpty() 
	{
		return _task_queue.empty();
	}

	static void* thr_start(void* arg) 
	{
		ThreadPool* tp = (ThreadPool*)arg;
		while (1) 
		{
			tp->LockQueue();
			while (tp->IsEmpty()) 
			{
				tp->ThreadWait();
			}

			ThreadTask* tt;
			tp->PopTask(&tt);
			tp->UnLockQueue();
			tt->Run();

			delete tt;
		}

		return NULL;
	}

public:
	ThreadPool(int max = MAX_THREAD) :_thread_max(max),
		_thread_cur(max),
		_tp_quit(false) 
	{
		pthread_mutex_init(&_lock, NULL);
		pthread_cond_init(&_cond, NULL);
	}

	~ThreadPool() 
	{
		pthread_mutex_destroy(&_lock);
		pthread_cond_destroy(&_cond);
	}

	bool PoolInit() 
	{
		pthread_t tid;
		for (int i = 0; i < _thread_max; i++) 
		{
			int ret = pthread_create(&tid, NULL, thr_start,
				this);
			if (ret != 0) 
			{
				std::cout << "create pool thread error
";

				return false;
			}
		}

		return true;
	}

	bool PushTask(ThreadTask* tt) 
	{
		LockQueue();
		if (_tp_quit) 
		{
			UnLockQueue();
			return false;
		}

		_task_queue.push(tt);
		WakeUpOne();
		UnLockQueue();

		return true;
	}

	bool PopTask(ThreadTask** tt) 
	{
		*tt = _task_queue.front();
		_task_queue.pop();

		return true;
	}

	bool PoolQuit() 
	{
		LockQueue();
		_tp_quit = true;
		UnLockQueue();
		while (_thread_cur > 0) 
		{
			WakeUpAll();
			usleep(1000);
		}

		return true;
	}

};
#endif

main.cpp

bool handler(int data)
{
	srand(time(NULL));
	int n = rand() % 5;
	printf("Thread: %p Run Tast: %d--sleep %d sec
", pthread_self(), data, n);
	sleep(n);

	return true;
}

int main()
{
	int i;
	ThreadPool pool;
	pool.PoolInit();
	for (i = 0; i < 10; i++) 
	{
		ThreadTask* tt = new ThreadTask(i, handler);
		pool.PushTask(tt);
	}

	pool.PoolQuit();

	return 0;
}
g++ -std=c++0x test.cpp -o test -pthread -lrt

4 -> 线程安全的单例模式

4.1 -> 什么是单例模式

单例模式(Singleton Pattern)是一种设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。这种模式常用于管理共享资源,如配置信息、线程池、缓存等。单例模式的核心思想是控制对象的实例化过程,使得在整个应用程序中只有一个实例存在,并且所有对该实例的访问都通过同一个访问点进行。

4.2 -> 单例模式的特点

  1. 唯一性:单例模式确保一个类只有一个实例。这意味着在整个应用程序的生命周期中,无论在何处调用该类的实例,都将返回同一个对象。
  2. 全局访问点:单例模式提供了一个全局访问点,通常是一个静态方法,用于获取该类的唯一实例。这使得在程序的任何地方都能够方便地访问该实例。
  3. 延迟初始化:单例模式的实例通常在第一次被请求时才会创建,这有助于节省资源。这种延迟初始化的特性也被称为懒加载(Lazy Initialization)。
  4. 线程安全:在多线程环境下,单例模式需要确保其唯一性和全局访问的正确性。这通常通过同步机制来实现,如使用锁或其他并发控制手段。
  5. 控制实例化:单例模式的构造函数通常是私有的,这防止了外部代码通过常规的构造函数创建新的实例。
  6. 可扩展性:单例模式可以通过继承或其他方式进行扩展,以满足不同的应用需求。
  7. 资源管理:单例模式常用于管理共享资源,如数据库连接、线程池、配置信息等,确保这些资源在整个应用程序中只有一个实例,从而提高资源的使用效率和管理便利性。
  8. 性能优化:由于单例模式只创建一个实例,因此可以减少内存开销和提高性能,特别是在处理大量数据或频繁访问的对象时。
  9. 简化代码结构:单例模式可以简化代码结构,因为它提供了一个单一的、全局的访问点,使得代码的维护和理解更加容易。
  10. 适用场景:单例模式适用于那些在整个应用程序中只需要一个实例的场景,如日志记录器、配置管理器、数据库连接池等。

4.3 -> 饿汉方式实现单例模式

template 
class Singleton 
{
	static T data;
public:
	static T* GetInstance() 
	{
		return &data;
	}
};

只要通过Singleton这个包装类来使用T对象,则一个进程中只有一个T对象的实例。

4.4 -> 懒汉方式实现单例模式

template 
class Singleton 
{
	static T* inst;
public:
	static T* GetInstance() 
	{
		if (inst == NULL) 
		{
			inst = new T();
		}
		return inst;
	}
};

存在一个严重的问题,线程不安全。

第一个调用GetInstance时,如果两个线程同时调用,可能会创建出两份T对象的实例。

但是后续再次调用,就没有问题了。

4.5 -> 懒汉方式实现单例模式(线程安全版本)

// 懒汉模式, 线程安全
template 
class Singleton 
{
	volatile static T* inst; // 需要设置 volatile 关键字, 否则可能被编译器优化.
	static std::mutex lock;

public:
	static T* GetInstance() 
	{
		if (inst == NULL) 
		{ // 双重判定空指针, 降低锁冲突的概率, 提高性能.
			lock.lock(); // 使用互斥锁, 保证多线程情况下也只调用一次 new.
			if (inst == NULL) 
			{
				inst = new T();
			}
			lock.unlock();
		}

		return inst;
	}
};

注意:

  1. 加锁解锁的位置
  2. 双重if判断,避免不必要的锁竞争
  3. volatile关键字防止过度优化

5 -> STL,智能指针和线程安全

5.1 -> STL中的容器是否是线程安全的

不是。

原因是,STL的设计初衷是将性能挖掘到极致,而一旦涉及到加锁保证线程安全,会对性能造成巨大的影响。

而且对于不同的容器,加锁方式的不同,性能可能也不同(例如hash表的锁表和锁桶)。

因此STL默认不是线程安全。如果需要在多线程环境下使用,往往需要调用者自行保证线程安全。

5.2 -> 智能指针是否是线程安全的

对于unique_ptr,由于只是在当前代码块范围内生效,因此不涉及线程安全问题。

对于shared_ptr,多个对象需要共用一个引用计数变量,所以会存在线程安全问题。但是标准库实现的时候考虑到了这个问题,基于原子操作(CAS)的方式保证shared_ptr能够高效,原子的操作引用计数。


感谢各位大佬支持!!!

互三啦!!!

本文地址:https://www.vps345.com/2944.html

搜索文章

Tags

PV计算 带宽计算 流量带宽 服务器带宽 上行带宽 上行速率 什么是上行带宽? CC攻击 攻击怎么办 流量攻击 DDOS攻击 服务器被攻击怎么办 源IP 服务器 linux 运维 游戏 云计算 ssh deepseek Ollama 模型联网 API CherryStudio python MCP 数据库 centos oracle 关系型 安全 分布式 llama 算法 opencv 自然语言处理 神经网络 语言模型 javascript 前端 chrome edge 进程 操作系统 进程控制 Ubuntu harmonyos 华为 开发语言 typescript 计算机网络 ubuntu 阿里云 网络 网络安全 网络协议 php android ollama ai 人工智能 llm Dell R750XS 自动化 蓝耘科技 元生代平台工作流 ComfyUI nuxt3 vue3 sql KingBase 银河麒麟 kylin v10 麒麟 v10 spring boot websocket docker 实时音视频 adb nginx 监控 自动化运维 node.js json html5 firefox django fastapi flask web3.py gitlab java numpy 笔记 C 环境变量 进程地址空间 RTSP xop RTP RTSPServer 推流 视频 kvm 无桌面 命令行 经验分享 react.js 前端面试题 持续部署 zotero WebDAV 同步失败 代理模式 c++ c语言 科技 个人开发 spring IIS .net core Hosting Bundle .NET Framework vs2022 windows 容器 vim jenkins gitee mysql 重启 排查 系统重启 日志 原因 macos 权限 tcp/ip ssl rust 媒体 深度优先 图论 并集查找 换根法 树上倍增 AI MNN DeepSeek Qwen 电脑 pppoe radius http ESP32 arm开发 架构 银河麒麟桌面操作系统 Kylin OS 国产化 智能路由器 IIS服务器 IIS性能 日志监控 react next.js 部署 部署next.js AI agent vue.js audio vue音乐播放器 vue播放音频文件 Audio音频播放器自定义样式 播放暂停进度条音量调节快进快退 自定义audio覆盖默认样式 java-ee udp 腾讯云 qt 多线程服务器 TCP服务器 qt项目 qt项目实战 qt教程 fpga开发 bash 小程序 YOLOv8 NPU Atlas800 A300I pro 国标28181 视频监控 监控接入 语音广播 流程 SIP SDP Docker Hub docker pull 镜像源 daemon.json Linux 根服务器 vscode 1.86 vscode redis uni-app 宝塔面板 同步 备份 建站 安全威胁分析 深度学习 YOLO 目标检测 计算机视觉 web安全 安全架构 mcp mcp-proxy mcp-inspector fastapi-mcp agent sse 豆瓣 追剧助手 迅雷 nas 微信 pytorch 内存 postman mock mock server 模拟服务器 mock服务器 Postman内置变量 Postman随机数据 LDAP https jvm kylin 爬虫 数据集 aws googlecloud 服务器繁忙 备选 网站 api 调用 示例 tomcat maven intellij idea 向日葵 filezilla 无法连接服务器 连接被服务器拒绝 vsftpd 331/530 openssl 密码学 excel 后端 MQTT mosquitto 消息队列 外网访问 内网穿透 端口映射 word图片自动上传 word一键转存 复制word图片 复制word图文 复制word公式 粘贴word图文 粘贴word公式 android studio 交互 sqlite spring cloud intellij-idea kafka hibernate shell 代码调试 ipdb pycharm conda pillow 统信 国产操作系统 虚拟机安装 游戏程序 网络结构图 ftp github git 远程工作 apache 孤岛惊魂4 kubernetes 学习方法 程序人生 golang 串口服务器 microsoft Linux网络编程 Docker Compose docker compose docker-compose JAVA IDEA Java DeepSeek-R1 API接口 Headless Linux ide Cursor pdf Dify 编辑器 live555 rtsp rtp visualstudio idm 银河麒麟操作系统 Hyper-V WinRM TrustedHosts pygame 小游戏 五子棋 嵌入式硬件 驱动开发 硬件工程 嵌入式实习 物联网 单片机 云原生 ci/cd ecmascript nextjs reactjs 网络工程师 华为认证 机器学习 僵尸进程 搜索引擎 HTML audio 控件组件 vue3 audio音乐播放器 Audio标签自定义样式默认 vue3播放音频文件音效音乐 自定义audio播放器样式 播放暂停调整声音大小下载文件 MI300x ux 多线程 svn string模拟实现 深拷贝 浅拷贝 经典的string类问题 三个swap 开发环境 SSL证书 报错 能力提升 面试宝典 技术 IT信息化 创意 社区 flash-attention c# Flask FastAPI Waitress Gunicorn uWSGI Uvicorn prometheus rpc 远程过程调用 Windows环境 直播推流 flutter ffmpeg 音视频 佛山戴尔服务器维修 佛山三水服务器维修 Reactor 设计模式 性能优化 C++ 联想开天P90Z装win10 matlab asi_bench mount挂载磁盘 wrong fs type LVM挂载磁盘 Centos7.9 eureka ecm bpm 运维开发 GaN HEMT 氮化镓 单粒子烧毁 辐射损伤 辐照效应 MCP server C/S LLM 博客 mongodb agi AIGC ansible playbook gpu算力 远程登录 telnet AI编程 华为云 面试 jdk 集成学习 集成测试 FunASR ASR rtsp服务器 rtsp server android rtsp服务 安卓rtsp服务器 移动端rtsp服务 大牛直播SDK 大数据 策略模式 单例模式 1024程序员节 ruoyi 前端框架 远程连接 rdp 实验 我的世界服务器搭建 群晖 linux 命令 sed 命令 虚拟化 半虚拟化 硬件虚拟化 Hypervisor 负载均衡 王者荣耀 Wi-Fi 信息与通信 chatgpt 微信小程序 minio Spring Security devops springboot 我的世界 我的世界联机 数码 .netcore jetty undertow Linux无人智慧超市 LInux多线程服务器 QT项目 LInux项目 单片机项目 UOS 统信操作系统 yum 医疗APP开发 app开发 ollama下载加速 大模型 oceanbase rc.local 开机自启 systemd 麒麟 系统安全 缓存 VMware安装mocOS VMware macOS系统安装 ISO镜像作为本地源 virtualenv 实习 云电竞 云电脑 todesk mac 模拟器 教程 学习 虚拟机 职场和发展 jupyter 备份SQL Server数据库 数据库备份 傲梅企业备份网络版 llama3 Chatglm 开源大模型 Windows 智能手机 NAS Termux Samba express gcc cursor ip stm32 W5500 OLED u8g2 金仓数据库 2025 征文 数据库平替用金仓 hugo 远程桌面 gaussdb wsl xss 宝塔面板访问不了 宝塔面板网站访问不了 宝塔面板怎么配置网站能访问 宝塔面板配置ip访问 宝塔面板配置域名访问教程 宝塔面板配置教程 pip QQ 聊天室 中兴光猫 换光猫 网络桥接 自己换光猫 fd 文件描述符 ocr 5G 3GPP 卫星通信 ArkUI 多端开发 智慧分发 应用生态 鸿蒙OS 思科模拟器 思科 Cisco Linux PID zabbix apt kind AI写作 AI作画 低代码 muduo 开源 其他 银河麒麟服务器操作系统 系统激活 华为od OD机试真题 华为OD机试真题 服务器能耗统计 jar visual studio code Radius 机器人 单元测试 功能测试 selenium ios EMQX 通信协议 Python基础 Python教程 Python技巧 弹性计算 KVM 计算虚拟化 弹性裸金属 智能音箱 智能家居 jmeter 软件测试 换源 国内源 Debian windwos防火墙 defender防火墙 win防火墙白名单 防火墙白名单效果 防火墙只允许指定应用上网 防火墙允许指定上网其它禁止 dify 阻塞队列 生产者消费者模型 服务器崩坏原因 测试工具 laravel vue css less XCC Lenovo grafana junit 繁忙 解决办法 替代网站 汇总推荐 AI推理 CDN SEO dba wsl2 漏洞 显示管理器 lightdm gdm ddos 文件系统 路径解析 网络穿透 云服务器 selete 高级IO HarmonyOS SSH Xterminal IM即时通讯 企业微信 剪切板对通 HTML FORMAT 流水线 脚本式流水线 efficientVIT YOLOv8替换主干网络 TOLOv8 k8s资源监控 annotations自动化 自动化监控 监控service 监控jvm embedding sqlserver openEuler elasticsearch unity unity3d log4j web r语言 okhttp CORS 跨域 游戏机 Netty 即时通信 NIO 微信分享 鸿蒙 Image wxopensdk dns HTTP 服务器控制 ESP32 DeepSeek can 线程池 LORA 大语言模型 NLP 图像处理 3d 反向代理 致远OA OA服务器 服务器磁盘扩容 HCIE 数通 VMware安装Ubuntu Ubuntu安装k8s k8s 图形化界面 C语言 linux上传下载 健康医疗 互联网医院 信号处理 vasp安装 查询数据库服务IP地址 SQL Server WSL2 语音识别 rclone AList webdav fnOS AutoDL rabbitmq hadoop 小艺 Pura X gateway Clion Nova ResharperC++引擎 Centos7 远程开发 业界资讯 客户端 SVN Server tortoise svn 数据挖掘 数据可视化 数据分析 系统架构 计算机 程序员 服务器主板 AI芯片 交换机 dubbo 硬件架构 历史版本 下载 安装 kamailio sip VoIP 数据结构 大数据平台 asm Ubuntu 24 常用命令 Ubuntu 24 Ubuntu vi 异常处理 烟花代码 烟花 元旦 debian etl safari Mac 系统 vSphere vCenter n8n dity make VR手套 数据手套 动捕手套 动捕数据手套 Java Applet URL操作 服务器建立 Socket编程 网络文件读取 armbian u-boot AI大模型 大模型入门 Deepseek 大模型教程 webstorm postgresql ukui 麒麟kylinos openeuler rust腐蚀 微服务 视觉检测 HarmonyOS Next .net db 回显服务器 UDP的API使用 数学建模 程序 升级 CVE-2024-7347 需求分析 规格说明书 安装教程 GPU环境配置 Ubuntu22 CUDA PyTorch Anaconda安装 输入法 远程 命令 执行 sshpass 操作 Kali big data list 模拟实现 opensearch helm 飞书 web3 自动驾驶 恒源云 游戏引擎 抗锯齿 tcp vscode1.86 1.86版本 ssh远程连接 autodl 课程设计 监控k8s集群 集群内prometheus IPMITOOL BMC 硬件管理 oneapi open webui IMM 无人机 cocoapods xcode gradle echarts SenseVoice 传统数据库升级 银行 LLMs 单一职责原则 Python 网络编程 聊天服务器 套接字 TCP Socket 移动云 云服务 可信计算技术 合成模型 扩散模型 图像生成 鲲鹏 FTP 服务器 KylinV10 麒麟操作系统 Vmware 鸿蒙系统 编程 性能分析 计算机外设 gitea asp.net大文件上传 asp.net大文件上传下载 asp.net大文件上传源码 ASP.NET断点续传 asp.net上传文件夹 asp.net上传大文件 .net core断点续传 中间件 iis ruby GCC aarch64 编译安装 HPC iBMC UltraISO nfs 实时互动 SSL 域名 rsyslog 虚拟局域网 TRAE DevEco Studio OpenHarmony 真机调试 sdkman 网络攻击模型 显卡驱动 Trae IDE AI 原生集成开发环境 Trae AI 工作流 workflow tcpdump 树莓派 VNC WSL win11 无法解析服务器的名称或地址 黑客技术 etcd 数据安全 RBAC yaml Ultralytics 可视化 Linux24.04 deepin 本地部署 相机 webrtc ui 微信小程序域名配置 微信小程序服务器域名 微信小程序合法域名 小程序配置业务域名 微信小程序需要域名吗 微信小程序添加域名 软件工程 EasyConnect Cline Kali Linux 黑客 渗透测试 信息收集 实战案例 iperf3 带宽测试 网工 虚幻 ssrf 失效的访问控制 openwrt ShenTong open Euler dde 统信UOS mysql离线安装 ubuntu22.04 mysql8.0 源码 毕业设计 LLM Web APP Streamlit webgl 线程 腾讯云大模型知识引擎 bug springboot远程调试 java项目远程debug docker远程debug java项目远程调试 springboot远程 考研 onlyoffice 在线office 游戏服务器 TrinityCore 魔兽世界 P2P HDLC sysctl.conf vm.nr_hugepages frp adobe elk hive Hive环境搭建 hive3环境 Hive远程模式 Linux的基础指令 odoo 服务器动作 Server action eclipse 环境迁移 redhat Ubuntu 24.04.1 轻量级服务器 python3.11 文件分享 Agent Linux awk awk函数 awk结构 awk内置变量 awk参数 awk脚本 awk详解 chrome 浏览器下载 chrome 下载安装 谷歌浏览器下载 wps 安卓 netty 昇腾 npu epoll sentinel CPU AI-native Docker Desktop 高效日志打印 串口通信日志 服务器日志 系统状态监控日志 异常记录日志 MySql yolov8 毕设 魔百盒刷机 移动魔百盒 机顶盒ROM linux安装配置 ESXi Dell HPE 联想 浪潮 Typore 微信公众平台 ipython Ubuntu DeepSeek DeepSeek Ubuntu DeepSeek 本地部署 DeepSeek 知识库 DeepSeek 私有化知识库 本地部署 DeepSeek DeepSeek 私有化部署 wireshark 嵌入式 linux驱动开发 seatunnel DigitalOcean GPU服务器购买 GPU服务器哪里有 GPU服务器 加解密 Yakit yaklang 多个客户端访问 IO多路复用 TCP相关API 温湿度数据上传到服务器 Arduino HTTP bootstrap html 软考 rag ragflow ragflow 源码启动 tailscale derp derper 中转 矩阵 C++软件实战问题排查经验分享 0xfeeefeee 0xcdcdcdcd 动态库加载失败 程序启动失败 程序运行权限 标准用户权限与管理员权限 deepseek r1 bonding 链路聚合 交叉编译 压力测试 开机自启动 mamba Vmamba windows日志 工业4.0 Minecraft 防火墙 NAT转发 NAT Server Unity Dedicated Server Host Client 无头主机 iphone stm32项目 Linux环境 unix freebsd glibc npm 小智AI服务端 xiaozhi TTS IMX317 MIPI H265 VCU 镜像 thingsboard lio-sam SLAM arm H3C iDRAC R720xd springsecurity6 oauth2 授权服务器 前后端分离 tensorflow dell服务器 go css3 navicat 服务器无法访问 ip地址无法访问 无法访问宝塔面板 宝塔面板打不开 XFS xfs文件系统损坏 I_O error ROS es HiCar CarLife+ CarPlay QT RK3588 iot Node-Red 编程工具 流编程 个人博客 X11 Xming 毕昇JDK xpath定位元素 MacOS录屏软件 file server http server web server 主板 电源 网卡 UDP langchain deep learning docker部署翻译组件 docker部署deepl docker搭建deepl java对接deepl 翻译组件使用 强化学习 CH340 串口驱动 CH341 uart 485 cmos 硬件 RoboVLM 通用机器人策略 VLA设计哲学 vlm fot robot 视觉语言动作模型 具身智能 超融合 DNS micropython esp32 mqtt RAGFLOW RAG 检索增强生成 文档解析 大模型垂直应用 热榜 RAID RAID技术 磁盘 存储 crosstool-ng c prompt easyui grub 版本升级 扩容 rpa 游戏开发 pgpool bcompare Beyond Compare 田俊楠 IPMI 带外管理 ros 设备 GPU PCI-Express 自动化任务管理 Ubuntu共享文件夹 共享目录 Linux共享文件夹 yum源切换 更换国内yum源 音乐服务器 Navidrome 音流 outlook ping++ minicom 串口调试工具 RAGFlow 磁盘镜像 服务器镜像 服务器实时复制 实时文件备份 车载系统 Erlang OTP gen_server 热代码交换 事务语义 ABAP 离线部署dify 大模型推理 大模型学习 chfs ubuntu 16.04 SSH 服务 SSH Server OpenSSH Server 远程控制 rustdesk neo4j 知识图谱 kali 共享文件夹 SWAT 配置文件 服务管理 网络共享 eNSP 企业网络规划 华为eNSP 网络规划 嵌入式Linux IPC dns是什么 如何设置电脑dns dns应该如何设置 ue5 vr DeepSeek行业应用 Heroku 网站部署 存储维护 NetApp存储 EMC存储 在线预览 xlsx xls文件 在浏览器直接打开解析xls表格 前端实现vue3打开excel 文件地址url或接口文档流二进 EMUI 回退 降级 gnu docker命令大全 LInux 测试用例 分布式训练 cuda 李心怡 cd 目录切换 springcloud 边缘计算 双系统 灵办AI 链表 Linux的权限 怎么卸载MySQL MySQL怎么卸载干净 MySQL卸载重新安装教程 MySQL5.7卸载 Linux卸载MySQL8.0 如何卸载MySQL教程 MySQL卸载与安装 算力 CentOS Stream CentOS edge浏览器 HAProxy 元服务 应用上架 YOLOv12 服务器数据恢复 数据恢复 存储数据恢复 北亚数据恢复 oracle数据恢复 物联网开发 clickhouse docker部署Python 社交电子 软件需求 西门子PLC 通讯 高效远程协作 TrustViewer体验 跨设备操作便利 智能远程控制 大模型面经 AnythingLLM AnythingLLM安装 软链接 硬链接 直流充电桩 充电桩 WebUI DeepSeek V3 蓝桥杯 环境配置 Claude trae wsgiref Web 服务器网关接口 影刀 #影刀RPA# 多层架构 解耦 skynet Nuxt.js 基础环境 ubuntu20.04 开机黑屏 deekseek 知识库 裸金属服务器 弹性裸金属服务器 cnn DenseNet p2p CrewAI 沙盒 word 阿里云ECS 增强现实 沉浸式体验 应用场景 技术实现 案例分析 AR 多路转接 Xinference AD域 ardunio BLE Redis Desktop 分析解读 USB网络共享 Playwright 自动化测试 大模型微调 风扇控制软件 ssh远程登录 虚幻引擎 DocFlow gpt 信号 figma ubuntu24 vivado24 智能硬件 MS Materials 模拟退火算法 vmware 卡死 EtherNet/IP串口网关 EIP转RS485 EIP转Modbus EtherNet/IP网关协议 EIP转RS485网关 EIP串口服务器 自动化编程 GoogLeNet code-server Google pay Apple pay 浏览器开发 AI浏览器 MacMini 迷你主机 mini Apple ssh漏洞 ssh9.9p2 CVE-2025-23419 wpf 宠物 免费学习 宠物领养 宠物平台 k8s集群资源管理 云原生开发 matplotlib AISphereButler ros2 moveit 机器人运动 银河麒麟高级服务器 外接硬盘 Kylin flink AI代码编辑器 信息可视化 网页设计 华为机试 ai小智 语音助手 ai小智配网 ai小智教程 esp32语音助手 diy语音助手 数据库系统 lsb_release /etc/issue /proc/version uname -r 查看ubuntu版本 做raid 装系统 tidb GLIBC nvidia remote-ssh 框架搭建 程序员创富 docker run 数据卷挂载 交互模式 VMware创建虚拟机 perf rime 搭建个人相关服务器 mcu sqlite3 火绒安全 VPS DBeaver 数据仓库 kerberos Windsurf 本地知识库部署 DeepSeek R1 模型 内网服务器 内网代理 内网通信 VM搭建win2012 win2012应急响应靶机搭建 攻击者获取服务器权限 上传wakaung病毒 应急响应并溯源 挖矿病毒处置 应急响应综合性靶场 命名管道 客户端与服务端通信 ip命令 新增网卡 新增IP 启动网卡 7z 技能大赛 信创 信创终端 中科方德 searxng av1 电视盒子 网络药理学 生物信息学 生信 PPI String Cytoscape CytoHubba RustDesk自建服务器 rustdesk服务器 docker rustdesk Docker引擎已经停止 Docker无法使用 WSL进度一直是0 镜像加速地址 sonoma 自动更新 linux环境变量 firewall xshell termius iterm2 openstack Xen 雨云 NPS 数据库开发 数据库架构 database PX4 WebRTC uniapp TCP协议 camera Arduino 电子信息 wordpress 无法访问wordpess后台 打开网站页面错乱 linux宝塔面板 wordpress更换服务器 composer 产测工具框架 IMX6ULL 管理框架 VLAN 企业网络 软件定义数据中心 sddc RTMP 应用层 keepalived ArcTS 登录 ArcUI GridItem arkUI 服务网格 istio Qwen2.5-coder 离线部署 opcua opcda KEPServer安装 js Logstash 日志采集 threejs 3D chrome devtools chromedriver nlp 开发 架构与原理 milvus C# MQTTS 双向认证 emqx token sas 序列化反序列化 spark HistoryServer Spark YARN jobhistory VSCode 数据管理 数据治理 数据编织 数据虚拟化 raid5数据恢复 磁盘阵列数据恢复 自定义客户端 SAS 鸿蒙开发 移动开发 EtherCAT转Modbus ECT转Modbus协议 EtherCAT转485网关 ECT转Modbus串口网关 EtherCAT转485协议 ECT转Modbus网关 dock 加速 nac 802.1 portal 僵尸世界大战 游戏服务器搭建 欧标 OCPP 政务 分布式系统 监控运维 Prometheus Grafana proxy模式 语法 zookeeper lua 服务器部署ai模型 vue-i18n 国际化多语言 vue2中英文切换详细教程 如何动态加载i18n语言包 把语言json放到服务器调用 前端调用api获取语言配置文件 Anolis nginx安装 环境安装 linux插件下载 黑苹果 ai工具 java-rocketmq v10 软件 ldap sequoiaDB fast 域名服务 DHCP 符号链接 配置 大模型应用 捆绑 链接 谷歌浏览器 youtube google gmail 音乐库 飞牛 实用教程 GIS 遥感 WebGIS OpenSSH 图形渲染 gpt-3 文心一言 trea idea 大大通 第三代半导体 碳化硅 cudnn 流式接口 URL prometheus数据采集 prometheus数据模型 prometheus特点 人工智能生成内容 项目部署到linux服务器 项目部署过程 金融 pyqt ceph 网络用户购物行为分析可视化平台 大数据毕业设计 Kylin-Server 服务器安装 内网环境 alias unalias 别名 h.264 IPv4 子网掩码 公网IP 私有IP SSH 密钥生成 SSH 公钥 私钥 生成 大文件分片上传断点续传及进度条 如何批量上传超大文件并显示进度 axios大文件切片上传详细教 node服务器合并切片 vue3大文件上传报错提示错误 大文件秒传跨域报错cors cpp-httplib 混合开发 JDK regedit 开机启动 压测 ECS 深度求索 私域 状态管理的 UDP 服务器 Arduino RTOS 拓扑图 SSE rocketmq 网卡的名称修改 eth0 ens33 GRUB引导 Linux技巧 mariadb docker搭建nacos详解 docker部署nacos docker安装nacos 腾讯云搭建nacos centos7搭建nacos 本地化部署 centos-root /dev/mapper yum clean all df -h / du -sh xrdp 技术共享 SRS 流媒体 直播 AP配网 AK配网 小程序AP配网和AK配网教程 WIFI设备配网小程序UDP开 京东云 jina 匿名管道 嵌入式系统开发 基础入门 Deepseek-R1 私有化部署 推理模型 QT 5.12.12 QT开发环境 Ubuntu18.04 代理服务器 私有化 项目部署 性能测试 雨云服务器 崖山数据库 YashanDB 玩机技巧 软件分享 软件图标 视频编解码 源码剖析 rtsp实现步骤 流媒体开发 NFS SysBench 基准测试 dash 正则表达式 uv mybatis 相差8小时 UTC 时间 远程看看 远程协助 远程服务 软负载 多进程 cpu 实时 使用 conda配置 conda镜像源 midjourney llama.cpp swoole 三级等保 服务器审计日志备份 USB转串口 飞牛NAS 飞牛OS MacBook Pro FTP服务器 AI Agent 字节智能运维 harmonyOS面试题 办公自动化 自动化生成 pdf教程 rnn 邮件APP 免费软件 win服务器架设 windows server risc-v 大模型部署 Ubuntu Server Ubuntu 22.04.5 迁移指南 IO模型 .net mvc断点续传 Invalid Host allowedHosts g++ g++13 Cookie visual studio 产品经理 干货分享 黑客工具 密码爆破 MDK 嵌入式开发工具 论文笔记 sublime text arcgis ubuntu24.04.1 状态模式 triton 模型分析 anaconda 线性代数 电商平台 运维监控 服务器时间 流量运营 服务器管理 配置教程 网站管理 剧本 ue4 着色器 seleium 宕机切换 服务器宕机 pyautogui uni-file-picker 拍摄从相册选择 uni.uploadFile H5上传图片 微信小程序上传图片 执法记录仪 智能安全帽 smarteye VS Code bot Docker make命令 makefile文件 transformer 粘包问题 leetcode 推荐算法 Linux find grep DOIT 四博智联 代理 iftop 网络流量监控 hexo PVE 系统开发 binder framework 源码环境 常用命令 文本命令 目录命令 AD 域管理 网站搭建 serv00 端口测试 微信开放平台 微信公众号配置 Unity插件 mm-wiki搭建 linux搭建mm-wiki mm-wiki搭建与使用 mm-wiki使用 mm-wiki详解 iventoy VmWare OpenEuler x64 SIGSEGV xmm0 磁盘监控 服务器配置 TrueLicense Ark-TS语言 firewalld Open WebUI Attention hosts 上传视频至服务器代码 vue3批量上传多个视频并预览 如何实现将本地视频上传到网页 element plu视频上传 ant design vue vue3本地上传视频及预览移除 minecraft bat 端口 查看 ss