【Linux筆記】——線程池項目與線程安全單例模式

🔥個人主頁🔥:孤寂大仙V
🌈收錄專欄🌈:Linux
🌹往期回顧🌹: 【Linux筆記】——簡單實習一個日志項目
🔖流水不爭,爭的是滔滔不息


  • 一、線程池設計
  • 二、線程池代碼
  • 三、線程安全的單例模式
  • 四、線程安全和重入問題

一、線程池設計

線程池

一種線程使用模式。線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。而線程池維護著多個線程,等待著監督管理者分配可并發執行的任務。這避免了在處理短時間任務時創建與銷毀線程的代價。線程池不僅能夠保證內核的充分利用,還能防止過分調度,可用線程數量應該取決于可用的并發處理器、處理器內核、內存、網絡sockets等的數量。線程池的主要優點是減少在創建和銷毀線程上所花的時間以及系統資源的開銷。通過重用已存在的線程,線程池可以顯著提高系統性能,特別是在需要處理大量短生命周期任務的場景中。

使用場景

需要大量的線程來完成任務,且完成任務的時間比較短。比如WEB服務器完成網頁請求這樣的任務,使用線程池技術是非常合適的。因為單個任務小,而任務數量巨大,你可以想象一個熱門網站的點擊次數。 但對于長時間的任務,比如一個Telnet連接請求,線程池的優點就不明顯了。因為Telnet會話時間比線程的創建時間大多了。

對性能要求苛刻的應用,比如要求服務器迅速響應客戶的請求。

接受突發性的大量請求,但不至于使服務器因此產生大量線程的應用。突發性大量客戶請求,在沒有線程池情況下,將產生大量線程,雖然理論上大部分操作系統線程數目最大值不是問題,短時間內產生大量線程可能使內存到達極限,出現錯誤。

在這里插入圖片描述

二、線程池代碼

ThreadPool.hpp線程池主體邏輯

#pragma once#include <iostream>
#include <vector>
#include <queue>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
using namespace std;
using namespace MutexModule;
using namespace CondModule;
using namespace ThreadModule;
using namespace LogModule;const int gnum=5;
namespace ThreadPoolModule
{template<class T>class ThreadPool{public:ThreadPool(int num=gnum):_num(num),_isrunning(false),_sleepnum(0){for(int i=0;i<_num;i++){_thread.emplace_back([this](){HandlerTask();});}}void Threadone(){_cond.Signal();LOG(LogLevel::INFO) << "喚醒一個休眠線程";}void Threadall(){LockGuard lockguard (_mutex);if(_sleepnum>0){_cond.Broadcast();}LOG(LogLevel :: INFO)<<"喚醒所有休眠線程";}void Start(){if(_isrunning) return;_isrunning=true;for(auto &thread :_thread){thread.Start();}LOG(LogLevel :: INFO)<<"開始創建線程池";}void Stop(){if(!_isrunning) return;_isrunning =false;Threadall();//讓等待的進程全部啟動}void Join(){for(auto &thread :_thread){thread.Join();}LOG(LogLevel :: INFO)<<"線程回收";}void HandlerTask(){char name[128];pthread_getname_np(pthread_self(), name, sizeof(name));while(true){T t;{LockGuard lockguard (_mutex);if(_taskq.empty() && _isrunning) //把全部休眠的線程啟動,必須保證進程池已經退出狀態,要不就陷入死循環{_sleepnum++;//如果等待計數++_cond.Wait(_mutex);_sleepnum--;//退出等待計數--}if(_taskq.empty() && !_isrunning) //等待后喚醒,必須是任務隊列為空 進程池退出{LOG(LogLevel :: INFO)<<name<<"退出了,任務隊列為空,進程池退出";break;}t=_taskq.front();_taskq.pop();}t();//執行任務}}bool Enqueue (const T& in){if(_isrunning){LockGuard LockGuard (_mutex);_taskq.push(in);if(_sleepnum==_thread.size()){Threadone();}return true;}return false;    }~ThreadPool(){}private:vector<Thread> _thread;queue<T> _taskq;int _num;Mutex _mutex;Cond _cond;bool _isrunning;int _sleepnum;};
}

這里用到了之前封裝好的線程、條件變量互斥與同步、日志。

私有成員變量
_thread我們用vector數組充當線程池,_taskq任務隊列用的是queue隊列,_num是線程池中的線程的個數(我們寫的是固定線程的線程池),_isrunning判斷線程是否運行,_sleepnum是線程等待的個數。


		ThreadPool(int num=gnum):_num(num),_isrunning(false),_sleepnum(0){for(int i=0;i<_num;i++){_thread.emplace_back([this](){HandlerTask();});}}void HandlerTask(){char name[128];pthread_getname_np(pthread_self(), name, sizeof(name));while(true){T t;{LockGuard lockguard (_mutex);if(_taskq.empty() && _isrunning) //把全部休眠的線程啟動,必須保證進程池已經退出狀態,要不就陷入死循環{_sleepnum++;//如果等待計數++_cond.Wait(_mutex);_sleepnum--;//退出等待計數--}if(_taskq.empty() && !_isrunning) //等待后喚醒,必須是任務隊列為空 進程池退出{LOG(LogLevel :: INFO)<<name<<"退出了,任務隊列為空,進程池退出";break;}t=_taskq.front();_taskq.pop();}t();//執行任務}

上面的代碼是構造函數構造線程池,這里主要闡述,構造函數創建線程池與線程去執行任務的函數的關系。
構造函數通過一個for循環,我們創建了_num個Thread對象,每個對象都綁定一個lambda,lambda里面調用的是線程池的成員函數HandlerTask()(在類內調用類內成員函數用lambda),這些lambda是“線程的入口函數”,它們一啟動就跑進HandlerTask()中并一直在那里循環干活。


void HandlerTask(){char name[128];pthread_getname_np(pthread_self(), name, sizeof(name));while(true){T t;{LockGuard lockguard (_mutex);if(_taskq.empty() && _isrunning) //把全部休眠的線程啟動,必須保證進程池已經退出狀態,要不就陷入死循環{_sleepnum++;//如果等待計數++_cond.Wait(_mutex);_sleepnum--;//退出等待計數--}if(_taskq.empty() && !_isrunning) //等待后喚醒,必須是任務隊列為空 進程池退出{LOG(LogLevel :: INFO)<<name<<"退出了,任務隊列為空,進程池退出";break;}t=_taskq.front();_taskq.pop();}t();//執行任務}

上面這段代碼。一啟動,線程池的每個線程都會進去,所以加鎖。如果任務隊列是空的并且這個線程池已經啟動了,那么線程就要進入等待隊列(條件變量同步),注意要寫一個計數的變量需要進入等待隊列就要++出來就–(這里+±-就是為了喚醒休眠線程的時候進行判斷,顆粒度更細)。如果任務隊列為空并且線程池也不在運行了,直接break退出回收線程。最后滿足線程能拿到任務,取出隊首的任務,執行任務。


		//喚醒單個線程void Threadone(){_cond.Signal();LOG(LogLevel::INFO) << "喚醒一個休眠線程";}//喚醒所有進程void Threadall(){LockGuard lockguard (_mutex);if(_sleepnum>0){_cond.Broadcast();}LOG(LogLevel :: INFO)<<"喚醒所有休眠線程";}//創建線程池void Start(){if(_isrunning) return;_isrunning=true;for(auto &thread :_thread){thread.Start();}LOG(LogLevel :: INFO)<<"開始創建線程池";}//終止線程void Stop(){if(!_isrunning) return;_isrunning =false;Threadall();//讓等待的進程全部啟動}//回收線程void Join(){for(auto &thread :_thread){thread.Join();}LOG(LogLevel :: INFO)<<"線程回收";}//任務隊列中放任務bool Enqueue (const T& in){if(_isrunning){LockGuard LockGuard (_mutex);_taskq.push(in);if(_sleepnum==_thread.size()){Threadone();}return true;}return false;    }

喚醒單個線程直接調用之前封裝好的條件變量同步,喚醒所有休眠的線程,如果之前計數的_sleepnum>0就要喚醒所有的休眠線程了。
啟動線程就是創建vector數組里的線程,創建線程池。
進程終止,調用之前封裝的條件變量。
回收線程,調用之前封裝的條件變量。

Enqueue就是往任務隊列里放任務,if(_sleepnum==_thread.size()) 判斷是否所有線程都在休眠,避免喚醒線程池中已經在忙的線程(節省上下文切換開銷)如果不判斷 _sleepnum,直接 Threadone() 會怎樣?可能會導致重復喚醒甚至無意義的上下文切換。比如:有5個線程,其中2個在處理任務,3個在睡覺;你來了個新任務,就喚醒1個線程;但其實可能原本某個線程馬上就處理完會搶新任務;你提前喚醒一個,就多了一次線程上下文切換(白喚醒了)。


main.cc

#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"using namespace LogModule;
using namespace ThreadPoolModule;int main()
{Enable_Console_Log_Strategy();int cnt=5;ThreadPool<task_t>* tp=new ThreadPool <task_t> ();tp->Start();while(cnt){sleep(1);tp->Enqueue(Download);cnt--;}tp->Stop();tp->Join();return 0;
}

Task.hpp

#pragma once
#include <iostream>
#include <functional>
#include "Log.hpp"using namespace LogModule;using task_t = std::function<void()>;void Download()
{LOG(LogLevel::DEBUG) << "我是一個下載任務...";
}

這里的function就簡單寫了一下,沒有放具體的任務,但是要走到這里用的是function的語法


線程池就是通過一個vector數組(這里這么寫的也可以是別的)里面創建線程,其實就是把線程準備好放在vector數組中,任務來了線程直接就能用,大大提高了效率。線程池就是提前創建一批線程放在池子里反復復用,避免任務來了才臨時創建/銷毀線程造成的高開銷。

在這里插入圖片描述

常規線程池:源碼

三、線程安全的單例模式

單例模式

單例模式是一種設計模式,確保一個類只有一個實例(對象),并提供一個全局訪問點來獲取該實例。這種模式通常用于控制資源的訪問,例如數據庫連接、日志記錄器等,以避免創建多個實例導致資源浪費或沖突。

餓漢模式實現單例
一開始就創建好單例對象。程序一啟動,就創建好對象了,餓的不行

懶漢模式實現單例
什么時候用什么時候創建單例對象。防止創建多個對象。第一次用對象是才創建,是不是很懶。


線程安全的懶漢式單例模式

禁用拷貝構造和賦值重載

ThreadPool(const ThreadPool<T> &) = delete;               // 把拷貝構造給禁用 沒辦法直接創建對象
ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // 把賦值重載禁用

顯示的禁掉拷貝構造和賦值重載,單例模式的最大的特征以及核心就是,保證全程只有一個對象。如果不禁掉拷貝構造和賦值重載就會出現多個對象,完全違背了單例迷失的初衷。
沒有禁用拷貝構造和賦值重載,單例根本不單例。


類外初始化單例指針

template <class T>
class ThreadPool {
public:static ThreadPool<T>* inc;// 其他成員...
};template <class T>
ThreadPool<T>* ThreadPool<T>::inc = nullptr;// 類外初始化template <class T>
Mutex ThreadPool<T>::_lock; // 類外初始化

類內聲明的靜態成員變量,必須類外初始化。 static ThreadPool* inc這個靜態成員變量必須類外初始化。原因是:靜態成員變量屬于類本身,而不是某個對象。類聲明只是告訴編譯器"這里有這么個靜態變量",但不分配內存。只有在類外定義后,編譯器才會給它分配內存空間。

為什么要加static成為靜態成員變量?


創建單例對象

static ThreadPool<T> *GetInstance(){if (inc == nullptr) // 多加一層多一層保護  {LockGuard lockguard(_lock); // 為防止多線程訪問,加鎖if (inc == nullptr) //第一次用這個inc單例指針對象{LOG(LogLevel::DEBUG) << "首次使用單例, 創建之....";inc = new ThreadPool<T>();  //創建單例指針對象}}return inc;}

這里主要就是創建單例指針對象,如果這里不加static如果想調用這個函數是不是就要創建這個類的對象就違背了單例的初衷。所以這里要讓它成為靜態成員函數。這也回答了上面為什么要用static成員變量,在這個靜態成員函數中創建單例指針對象,要使用靜態成員變量。
內層中的if (inc == nullptr)是判斷是否是第一次用這個inc單例指針對象,如果沒有創建單例指針對象。加鎖是為了防止多線程訪問。在外層的if (inc == nullptr),首先不加外層的這個if (inc == nullptr),線程來了訪問互斥鎖,拿到鎖的線程進去創建單例指針對象,那么多線程是不是每次不管是否已經創建了單例指針對象都要拿鎖然后進去轉一圈在返回這個單例指針對象,是不是效率就會降低。加上最外側的if (inc == nullptr) 每次線程來了,如果已經有單例指針對象了就不要進去在轉一圈,直接返回對象就完了,大大提高了效率。


Main.cc

#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"using namespace LogModule;
using namespace ThreadPoolModule;int main()
{Enable_Console_Log_Strategy();//int cnt=5;// ThreadPool<task_t>* tp=new ThreadPool <task_t> ();ThreadPool<task_t> ::GetInstance()->Start();while(cnt){sleep(1);ThreadPool<task_t> ::GetInstance()->Enqueue(Download);cnt--;}ThreadPool<task_t> ::GetInstance()->Stop();ThreadPool<task_t> ::GetInstance()->Join();return 0;
}

單例模式如何訪問類內成員函數呢?不用創建類的對象,直接用作用域解釋符,調用靜態成員函數。這樣獲取進程池的唯一對象,然后通過指針調用方法創建線程池。

懶漢式單例模式線程池

#pragma once#include <iostream>
#include <vector>
#include <queue>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
using namespace std;
using namespace MutexModule;
using namespace CondModule;
using namespace ThreadModule;
using namespace LogModule;const int gnum = 5;
namespace ThreadPoolModule
{template <class T>class ThreadPool{private:ThreadPool(int num = gnum): _num(num), _isrunning(false), _sleepnum(0){for (int i = 0; i < _num; i++){_thread.emplace_back([this](){ HandlerTask(); });}}void Threadone(){_cond.Signal();LOG(LogLevel::INFO) << "喚醒一個休眠線程";}void Threadall(){LockGuard lockguard(_mutex);if (_sleepnum > 0){_cond.Broadcast();}LOG(LogLevel ::INFO) << "喚醒所有休眠線程";}ThreadPool(const ThreadPool<T> &) = delete;               // 把拷貝構造給禁用 沒辦法直接創建對象ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // 把賦值重載禁用public:static ThreadPool<T> *GetInstance(){if (inc == nullptr) // 多加一層多一層保護  {LockGuard lockguard(_lock); // 為防止多線程訪問,加鎖if (inc == nullptr) //第一次用這個inc單例指針對象{LOG(LogLevel::DEBUG) << "首次使用單例, 創建之....";inc = new ThreadPool<T>();  //創建單例指針對象}}return inc;}void Start(){if (_isrunning)return;_isrunning = true;for (auto &thread : _thread){thread.Start();}LOG(LogLevel ::INFO) << "開始創建線程池";}void Stop(){if (!_isrunning)return;_isrunning = false;Threadall(); // 讓等待的進程全部啟動}void Join(){for (auto &thread : _thread){thread.Join();}LOG(LogLevel ::INFO) << "線程回收";}void HandlerTask(){char name[128];pthread_getname_np(pthread_self(), name, sizeof(name));while (true){T t;{LockGuard lockguard(_mutex);if (_taskq.empty() && _isrunning) // 把全部休眠的線程啟動,必須保證進程池已經退出狀態,要不就陷入死循環{_sleepnum++; // 如果等待計數++_cond.Wait(_mutex);_sleepnum--; // 退出等待計數--}if (_taskq.empty() && !_isrunning) // 等待后喚醒,必須是任務隊列為空 進程池退出{LOG(LogLevel ::INFO) << name << "退出了,任務隊列為空,進程池退出";break;}t = _taskq.front();_taskq.pop();}t(); // 執行任務}}bool Enqueue(const T &in){if (_isrunning){LockGuard LockGuard(_mutex);_taskq.push(in);if (_sleepnum == _thread.size()){Threadone();}return true;}return false;}~ThreadPool(){}private:vector<Thread> _thread;queue<T> _taskq;int _num;Mutex _mutex;Cond _cond;bool _isrunning;int _sleepnum;static ThreadPool<T> *inc;static Mutex _lock;};template <class T>ThreadPool<T> *ThreadPool<T>::inc = nullptr; // 類外初始化template <class T>Mutex ThreadPool<T>::_lock; // 類外初始化
}

運行結果
在這里插入圖片描述
懶漢式單例模式線程池:源碼

四、線程安全和重入問題

線程安全:就是多個線程在訪問共享資源時,能夠正確地執行,不會相互干擾或破壞彼此的執行結果。?般而言,多個線程并發同一段只有局部變量的代碼時,不會出現不同的結果。但是對全局變量或者靜態變量進行操作,并且沒有鎖保護的情況下,容易出現該問題。

重入:同?個函數被不同的執行流調用,當前?個流程還沒有執行完,就有其他的執行流再次進入,我們稱之為重入。?個函數在重?的情況下,運行結果不會出現任何不同或者任何問題,則該函數被稱為可重入函數,否則,是不可重入函數。學到現在,其實我們已經能理解重入其實可以分為兩種情況1.多線程重入函數2.信號導致?個執行流重復進入函數


可重入與線程安全聯系

  • 函數是可重入的,那就是線程安全的(其實知道這?句話就夠了)
  • 函數是不可重入的,那就不能由多個線程使用,有可能引發線程安全問題
  • 如果?個函數中有全局變量,那么這個函數既不是線程安全也不是可重人的。

可重入與線程安全區別

  • 可重入函數是線程安全函數的?種
  • 線程安全不一定是可重入的,而可重入函數則?定是線程安全的。
  • 如果將對臨界資源的訪問加上鎖,則這個函數是線程安全的,但如果這個重入函數若鎖還未釋放則會產生死鎖,因此是不可重入的。

如果不考慮信號導致一個執行流重復進入函數這種重入情況,線程安全和重入在安全角度不做區分。
但是線程安全側重說明線程訪問公共資源的安全情況,表現的是并發線程的特點。
可重入描述的是一個函數是否被重復進入,表示的是函數特點。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80952.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80952.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80952.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

28-FreeRTOS內核控制-延時-臨界區

一、FreeRTOS的內核控制接口分析 1.1 函數taskYIELD 此函數用于進行任務切換&#xff0c;此函數本質上是一個宏。它允許當前任務主動放棄CPU使用權&#xff0c;將控制權轉移給調度器&#xff0c;以便調度器可以選擇另一個就緒任務運行。taskYIELD通常用于協作式多任務系統中&am…

NtfsLookupAttributeByName函數分析之和Scb->AttributeName的關系

第一部分&#xff1a; VOID FindFirstIndexEntry ( IN PIRP_CONTEXT IrpContext, IN PSCB Scb, IN PVOID Value, IN OUT PINDEX_CONTEXT IndexContext ) { 。。。。。。 // // Lookup the attribute record from the Scb. // if (!NtfsLookupAt…

關閉 Ubuntu 20.04 的 GNOME Shell和PulseAudio

一、GNOME Shell GNOME Shell 是 Ubuntu 20.04 默認的桌面環境管理器。關閉它會失去圖形界面&#xff08;回到純終端模式&#xff09;&#xff0c;但可以節省內存和 CPU 資源。 方法 1&#xff1a;臨時關閉&#xff08;當前會話生效&#xff09; sudo systemctl stop gdm #…

Dijkstra算法——不帶負權的單源最短路徑

目錄 算法學習 算法原理 稠密圖Dijkstra模板 稀疏圖Dijkstra模板 練習 1 網絡延遲時間 2 到達最后一個房間的最少時間Ⅰ 3 到達最后一個房間的最少時間Ⅱ 4 訪問消失節點的最少時間 5 設計可以求最短路徑的圖類 6 概率最大的路徑 7 最小體力消耗路徑 8 從第一個節…

【安全攻防與漏洞?】??Heartbleed漏洞復現與修復

Heartbleed漏洞復現與修復 一、漏洞原理 Heartbleed漏洞&#xff08;CVE-2014-0160&#xff09; 是 OpenSSL 1.0.1 至 1.0.1f 版本中的一個嚴重內存泄漏漏洞。它源于 TLS 心跳擴展&#xff08;Heartbeat Extension&#xff09;協議中對請求長度字段的未校驗&#xff0c;導致攻…

力扣-最大連續一的個數

1.題目描述 2.題目鏈接 1004. 最大連續1的個數 III - 力扣&#xff08;LeetCode&#xff09; 3.代碼解答 class Solution {public int longestOnes(int[] nums, int k) {int zero0,length0;for(int left0,right0;right<nums.length;right){if(nums[right]0){zero;}while…

虛擬機Centos7:Cannot find a valid baseurl for repo: base/7/x86_64問題解決

問題 解決&#xff1a;更新yum倉庫源 # 備份現有yum配置文件 sudo cp -r /etc/yum.repos.d /etc/yum.repos.d.backup# 編輯CentOS-Base.repo文件 vi /etc/yum.repos.d/CentOS-Base.repo[base] nameCentOS-$releasever - Base baseurlhttp://mirrors.aliyun.com/centos/$relea…

Node.js 庫大全

在當今快速迭代的軟件開發領域&#xff0c;Node.js 憑借其強大的異步 I/O 處理能力和繁榮的生態系統&#xff0c;已成為全棧開發的核心技術。社區中涌現的無數實用庫&#xff0c;如同開發者手中的“瑞士軍刀”&#xff0c;能顯著提升效率、優化性能并保障安全。本文將系統梳理 …

如何評估物聯網框架的交互體驗?

物聯網&#xff08;IoT&#xff09;技術的快速發展推動了各類物聯網框架的涌現&#xff0c;但如何評估其交互體驗卻成為開發者和企業面臨的重要挑戰。交互體驗不僅涉及用戶界面&#xff08;UI&#xff09;的直觀性&#xff0c;還包括設備接入效率、協議兼容性、數據交互流暢度以…

3D個人簡歷網站 6.彈出框

3D個人簡歷網站 6.彈出框 在components下創建HomeInfo.jsx用于控制主頁彈出框信息 輸入rafce快速生成代碼塊 import React from reactconst HomeInfo () > {return (<div>HomeInfo</div>) }export default HomeInfo修改Home.jsx代碼實現彈出簡單效果 ……re…

在 ABP VNext 中集成 OpenCvSharp:構建高可用圖像灰度、壓縮與格式轉換服務

&#x1f680; 在 ABP VNext 中集成 OpenCvSharp&#xff1a;構建高可用圖像灰度、壓縮與格式轉換服務 &#x1f389; &#x1f4da; 目錄 &#x1f680; 在 ABP VNext 中集成 OpenCvSharp&#xff1a;構建高可用圖像灰度、壓縮與格式轉換服務 &#x1f389;&#x1f3af; 一、…

C++之STL--string

string 深入探索 C STL 中的 std::string一、std::string 的基本概念1. 內存管理2. 安全性 二、std::string 的構造與初始化1. 默認構造2. 從 C 風格字符串構造3. 從字符串的一部分構造4. 使用重復字符構造 三、std::string 的常用操作1. 字符串拼接2. 字符串比較3. 字符串查找…

網絡層——螞蟻和信鴿的關系VS路由原理和相關配置

前言&#xff08;&#x1f41c;??&#x1f54a;?&#xff09; 今天內容的主角是螞蟻&#xff08;動態路由&#xff09;和信鴿&#xff08;靜態路由&#xff09;&#xff0c;為什么這么說呢&#xff0c;來看一則小故事吧。 森林里&#xff0c;森林郵局要送一份重要信件&am…

在 Excel xll 自動注冊操作 中使用東方仙盟軟件2————仙盟創夢IDE

// 獲取當前工作表名稱string sheetName (string)XlCall.Excel(XlCall.xlfGetDocument, 7);// 構造動態名稱&#xff08;例如&#xff1a;Sheet1!MyNamedCell&#xff09;string fullName $"{sheetName}!MyNamedCell";// 獲取引用并設置值var namedRange (ExcelRe…

nginx日志

目錄 實驗要求&#xff1a; 實驗1&#xff1a; 1.使用vim打開/etc/nginx/nginx.conf查看內容 2.重新讀取文件并且重啟軟件 3.實時查看nginx日志 實驗2&#xff1a; 1.使用vim打開/etc/rsyslog.conf 2.配置此文件 3.保存退出后&#xff0c;將核心防護與防火墻關閉。 4.…

【高德開放平臺-注冊安全分析報告】

前言 由于網站注冊入口容易被黑客攻擊&#xff0c;存在如下安全問題&#xff1a; 暴力破解密碼&#xff0c;造成用戶信息泄露短信盜刷的安全問題&#xff0c;影響業務及導致用戶投訴帶來經濟損失&#xff0c;尤其是后付費客戶&#xff0c;風險巨大&#xff0c;造成虧損無底洞…

2024 CKA模擬系統制作 | Step-By-Step | 3、CKA考試系統的技術設置

目錄 免費獲取題庫配套 CKA_v1.31_模擬系統 一、免費提權配置 1、使用vim 編輯/etc/sudoers 二、安裝命令 1、安裝運行時接口命令 2、安裝Etcd命令 3、配置K8S命令自動補全 三、配置Kubectl 訪問集群 1、Master節點 2、Node01節點 四、SSH配置 1、Node01節點candi…

微信小程序請求扣子(coze)api的例子

1. 準備工作 在開始之前&#xff0c;確保已經完成了以下準備工作&#xff1a; 創建并發布了 Coze 智能體。獲取了個人訪問令牌&#xff08;Personal Access Token&#xff09;&#xff0c;這是用于授權的關鍵憑證。確認目標智能體的 Bot ID 和其他必要參數已準備就緒。 2. 請…

visual studio重新安裝如何修改共享組件、工具和SDK路徑方案

安裝了VsStudio后,如果自己修改了Shared路徑&#xff0c;當卸載舊版本&#xff0c;需要安裝新版本時發現&#xff0c;之前的Shared路徑無法進行修改&#xff0c;這就很坑了 但是卻遇到了路徑無法修改的問題…真讓人頭大&#xff0c;當然不修改也可以&#xff0c;有時候&#x…

【Python 算法零基礎 4.排序 ② 冒泡排序】

目錄 一、引言 二、算法思想 三、時間復雜度和空間復雜度 1.時間復雜度 2.空間復雜度 四、冒泡排序的優缺點 1.算法的優點 2.算法的缺點 五、實戰練習 88. 合并兩個有序數組 算法與思路 ① 合并數組 ② 冒泡排序 2148. 元素計數 算法與思路 ① 排序 ② 初始化計數器 ③ 遍歷數組…