【Linux系統】初見線程,概念與控制

前言:

? ? ? ? 上文我們講到了進程間信號的話題【Linux系統】萬字解析,進程間的信號-CSDN博客

? ? ? ? 本文我們再來認識一下:線程!


Linux線程概念

什么是線程

概念定義:

? ? ? ? 進程=內核數據結構+代碼和數據(執行流)

? ? ? ? 線程=是進程內部的一個執行分支(執行流)

內核與資源角度:

? ? ? ? 進程=分配系統資源的基本實體。

? ? ? ? 線程=CUP調度的基本單位。

初步理解線程:

? ? ? ? 在之前我們講過,進程=PCB(task_struct)+代碼和數據,如上圖所示。

? ? ? ? 而線程是什么呢?

? ? ? ? 線程是進程的一個個分支!一個線程 = 一個PCB+一份自己需要執行的代碼和數據!不同的線程執行進程中不同的代碼,各司其職。

? ? ? ? 一個線程執行一部分代碼,多個線程同時執行,讓進程整體效率提升!

? ? ? ? 而我們之前所講的進程其實是:內部只有一個線程的進程!(單線程)

結論:

? ? ? ? 1.線程也采用PCB結構體來描述的

? ? ? ? 2.對資源的劃分,本質是對虛擬地址的劃分。也就是說,虛擬地址就是資源的代表。

? ? ? ? 3線程對進程代碼的“劃分”,不需要我們人為的去“劃分”!因為進程所要執行的代碼,其本質都是由一個個函數組成的!這本就是天然的“劃分”好了的狀態,所以線程對函數“劃分”即可(獲得函數的入口地址即可)!

? ? ? ? 4.線程其實不會對資源進行劃分,進程內的大部分資源都是共享的,不存在說這個資源是線程a的誰都不可以訪問!對代碼的“劃分”也不是真正的劃分,僅僅是表示對任務的分配。一個線程負責執行一部分代碼,讓進程的代碼同時被多個線程推進!

? ? ? ? 5.Linux的線程就是輕量級的進程(單線程進程)!

? ? ? ? 6.進程強調獨占,部分共享(如進程間的通信)

? ? ? ? ? ?線程強調共享,部分獨占

補充:

? ? ? ? windows下的線程設計,與Linux的并不相同!Linux的線程都是使用PCB結構體描述的,但是windows下的線程是采用新設計的結構體:TCB來描述的。

? ? ? ? 越復雜的代碼可維護性、健壯性越不好,所以Linux在這一方面采用復用的方式,設計的更好!

分頁式存儲管理

進一步理解線程:內核資源的劃分

物理內存管理

? ? ? ? 物理內存最小管理與分配單位:頁框/頁幀,大小為4KB。當然虛擬內存是與物理內存一一對應的,虛擬內存也是以4KB為基本單位進行分配(是分配噢,不是讀寫)

? ? ? ? 之前我們在文件系統中也講過:磁盤數據的分配讀寫(磁盤是例外),是以4KB為單位進行的。【Linux系統】詳解Ext2,文件系統-CSDN博客

????????????????????????????????虛擬頁面(4KB) ? 物理頁框(4KB) ? 磁盤塊(4KB)

????????當然不是真的劃分為一個個4KB的空間,實際上是一個整體,只是OS在邏輯上進行了劃分。

? ? ? ? OS采用結構體:page,進行描述!

? ? ? ? page描述了頁框的各種信號,其中包含了頁框的狀態:是否被使用,是否被鎖定等等。

? ? ? ? 并采用數組:struct page mem[1048576],進行組織!

? ? ? ? 所以每一個page都會對應一個數組下標!而我們讓數組下標 * 4KB就可以得到page的起始首物理地址了!

? ? ? ? 起始首地址+頁框中的偏移量=真實的物理地址

????????

? ? ? ? 有了以上的梳理,我可以知道,當線程或進程申請物理內存時:

? ? ? ? 1.查數組,修改page? ? ? ? 2.建立page與內核數據結構的映射關系

頁表

重新認識頁表

? ? ? ? 在此之前,我們認識頁表就如圖所示:一張表保存虛擬地址與物理地址映射關系。

? ? ? ? 思考一個問題:

? ? ? ? ? ? ? ? 如果一張頁表將虛擬地址與物理地址的映射關系全部保存,(以32位機器為例)一個地址是4字節,那么頁表中一排就要保存8字節數據。那么一共有多少地址需要我們保存呢?4GB!這也就意味著頁表的大小將會來到:8字節 * 4GB = 32GB!這是不現實的!所以頁表是絕不可能僅用一張表來保存映射關系的。

頁表真正的保存方式:

? ? ? ? 真正的頁表由兩部分組成:頁目錄頁表

?虛擬地址的轉化:

????????首先將一個虛擬地址劃分位3部分:以10位、10位、12位為3組(32位下)??

? ? ? ? 前10位:表示指向頁目錄的地址,其中頁目錄中保存的是頁表的地址

? ? ? ? 中間10位:表示指向頁表的地址,其中頁表中保存的是頁地址(起始地址)。

? ? ? ? 最后12位:表示頁中的偏移量,前面的地址找到了具體的頁框,最后加上偏移量,就得到了真正的物理地址了!

細節:

? ? ? ? 1.一張頁目錄+n張頁表構成了映射體系,物理頁框是映射目標。最后12位地址+頁框地址=真實的物理地址

? ? ? ? 2.虛擬地址的轉化其實是有CPU中的硬件:MMU自動完成

? ? ? ? 3.申請物理內存:查找數組,找到沒有使用的page,修改page,通過page下標得到物理地址,以頁框位最小單位獲得到申請的內存。

? ? ? ? 4.寫時拷貝,缺頁中斷,內存申請等等,背后都可能要重新建立新的頁表與新的映射關系。????????

? ? ? ? 5.為什么要用最后12位,最為頁內偏移量?

? ? ? ? ? ? ? ? 12位:2^12,且一個地址的存儲空間為1字節,剛好為4KB(與頁框大小一致,可以覆蓋整個頁框的偏移)

? ? ? ? ????????最后12位:前20位的數據是一致的,這可以保證查找到數據屬于同一個4KB的頁框。

深刻理解線程

????????1.線程進行資源的劃分:本質是劃分地址空間,得到一定合法范圍的虛擬地址空間,本質就是,對頁表的劃分!

????????2. 線程對資源的共享:本質就是地址空間的共享,本質就是對頁表條目的共享!

????????3.線程是輕量化進程,顧名思義:線程的開銷比進程更低,尤其在線程的切換方面!

切換方面解釋:

? ? ? ? 為了提高轉化地址的效率,MMU引入了TLB(Translation Lookaside Buffer,緩存),其中存儲最近頻繁使用的映射關系!MMU做虛擬地址與物理地址的轉化時,先去TLB中查詢,若沒有,則再去頁表中查詢!

? ? ? ? 對于線程:線程不論如何切換,都是在同一個進程中的!在同一個虛擬地址空間中!

? ? ? ? 對于進程:進程一旦切換,新的進程是對應新的虛擬地址空間的!

? ? ? ? 也就是說,線程切換,虛擬地址空間不會切換,TLB正常使用!但進程切換,虛擬地址空間也切換,TLB中保存的映射關系全部報廢!需要全部將其刷新!

? ? ? ? 所以這也就是為什么線程的切換開銷更小!


Linux線程控制

引入pthread庫

這個一個關于線程的庫

? ? ? ? 首先,prhread庫是Linux系統下C/C++實現的線程庫

? ? ? ? 其次,Linux系統中其實并沒有真正的線程!都是輕量級進程!Linux 內核中沒有獨立的 “線程” 數據結構,而是通過 “輕量級進程(Lightweight Process, LWP)” 來實現線程功能!

? ? ? ? 但對于用戶來說,用戶需要使用線程的概念以及方法!所以為什么保證用戶的正常使用,C/C++實現了pthread庫,封裝了LWP,來實現“線程”的概念以及方法!

? ? ? ? 所以Linux線程的實現是在用戶層的,我們也將其稱為:用戶級線程

? ? ? ? 注:使用pthread庫,在編譯器時需要加上 -l pthread選項(因為pthread庫不是被默認鏈接的)

pthread庫接口

1.線程創建
pthread_create

功能:創建線程

#include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg);thread:輸出參數,用于存儲新線程的 ID(pthread_t 類型)
attr:線程屬性(如棧大小、分離狀態等),NULL 表示使用默認屬性
start_routine:線程入口函數(函數指針),格式為 void* (*)(void*),線程啟動后會執行該函數
arg:傳遞給 start_routine 的參數(無參數時傳 NULL)返回值:0:成功;非 0:錯誤碼

演示:

#include <pthread.h> //線程庫
#include <iostream>
using namespace std;void *routine(void *args)
{string name = static_cast<char *>(args);cout << "新線程:" << name << endl;while (true){}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void *)"thread -1");cout << "主線程" << endl;while (true){}
}hyc@hyc-alicloud:~/linux/線程dome$ ./test
主線程
新線程:thread -1

? ? ? ? 可以看到,其實創建了線程!我們也可以通過指令:ps -aL來查看:

hyc@hyc-alicloud:~/linux/線程dome$ ps -aLPID     LWP TTY          TIME CMD94651   94651 pts/0    00:00:21 test94651   94652 pts/0    00:00:21 test

? ? ? ? PID:我們可以看到PID都是一樣的!這說明都屬于同一個進程!

? ? ? ? LWP:LWP不一樣,這正好說明了創建了新的線程!

補充:

函數:pthread_create(創建線程),其底層其實封裝了系統調用:clone(創建輕量級進程)#include <sched.h>int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...,* pid_t *parent_tid,void *tls, pid_t *child_tid */ );

線程運行問題
創建新線程后,是先執行主線程還是先執行新線程的代碼?這個是不確定的,取決于OS的調用機制!
?CPU在調度的時候,是調度進程還是線程?線程!線程是CPU調度的基本單位!
?一個進程有多個線程,那么時間片如何分配?平均分配!
?線程運行時如果出現異常整個進程都會被OS直接終止掉!這也就導致了多線程程序的健壯性低。

2.線程終止
?pthread_exit

功能:終止線程

#include <pthread.h>void pthread_exit(void *retval);retval:一個 void* 類型的指針,表示線程退出的返回值return也可以終止線程,推薦使用:return
區別:在主線程中使用return,回讓整個進程全部退出!但pthread_exit只會退出主線程,其他子線程照常運行注:線程中萬不可用exit()退出!因為exit()是進程退出的接口!
pthread_cancel

功能:取消線程

#include <pthread.h>int pthread_cancel(pthread_t thread);線程取消后,退出結果是-1【PTHREAD_CANCELED】thread:目標線程的id(由pthread_create得到)
返回值:成功返回 0;失敗返回非 0 的錯誤碼(如 ESRCH 表示目標線程不存在)
注意:該函數只是 “請求” 取消,而非強制終止。目標線程是否以及何時終止,取決于其自身的取消配置

演示:

#include <pthread.h> //線程庫
#include <iostream>
using namespace std;void *routine(void *args)
{string name = static_cast<char *>(args);cout << "新線程:" << name << endl;while (true){}// 不應該看見cout << "線程取消失敗!" << endl;return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void *)"thread -1");// 取消線程pthread_cancel(tid);cout << "主線程" << endl;
}hyc@hyc-alicloud:~/linux/線程dome$ ./test
主線程
hyc@hyc-alicloud:~/linux/線程dome$

? ? ? ? 按道理來講,主線程也可以被取消,但并不建議這么做!

3.線程等待
pthread_join

功能:等待線程

其目的與進程的等待一致,都是為了獲得線程的返回值,并回收資源若不回收將回出現:內存泄漏!

#include <pthread.h>int pthread_join(pthread_t thread, void **retval);thread:需要等待的目標線程的 ID(由 pthread_create 函數返回)
retval:二級指針(void**),用于接收目標線程的退出狀態(即線程通過 pthread_exit(retval) 或 return retval 返回的值)
若不需要獲取退出狀態,可傳入 NULL
若需要獲取,則需提前定義一個 void* 指針,再將其地址傳給 retval返回值:0表示等待成功,非0表示不成功!

? ? ? ? 值得一提的是,此接口的等待方式的阻塞等待

演示:

#include <pthread.h> //線程庫
#include <iostream>
#include <unistd.h>
using namespace std;// 線程等待void *routine(void *agrs)
{string name = static_cast<char *>(agrs);cout << "新線程執行完方法,返回" << endl;return (void *)1;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void *)"thread");// 阻塞等待void *ret;pthread_join(tid, &ret);cout << "等待成功:" << (long long)ret << endl; // long long防止在64位下進度丟失int cnt = 5;while (cnt--){cout << "主線程運行中" << endl;sleep(1);}
}hyc@hyc-alicloud:~/linux/線程dome$ ./test
新線程執行完方法,返回
等待成功:1
主線程運行中
主線程運行中
主線程運行中
主線程運行中
主線程運行中
hyc@hyc-alicloud:~/linux/線程dome$

4.線程分離
pthread_detach

功能:讓新線程與主線程分離,分離主線程不再阻塞等待新線程了,新線程執行完畢后會自動的回收空間

? ? ? ? 當我們不關心新線程的返回值時,可以讓線程分離,這樣的好處是主線程不用阻塞的等待新線程,可以執行自己的代碼。

#include <pthread.h>int pthread_detach(pthread_t thread);thread 是需要分離的線程 ID(由 pthread_create 創建線程時返回)
成功返回 0;失敗返回非零錯誤碼注:即使線程分離了,分離的線程仍然都在同一個進程的地址空間中,所有的資源依舊可以訪問!分離的線程,不用被主線程join,也不能被主線程join(會失敗)!

演示:

#include <pthread.h> //線程庫
#include <iostream>
#include <unistd.h>
using namespace std;// 線程分離void *routine(void *agrs)
{int cnt = 5;while (cnt--){cout << "新線程運行" << endl;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void *)"thread");cout << "運行主線程" << endl;// 分離pthread_detach(tid);// 等待失敗!!!int ret = pthread_join(tid, nullptr);if (ret != 0)cout << "等待失敗!" << endl;
}hyc@hyc-alicloud:~/linux/線程dome$ make
g++ -o test test.cc -l pthread
hyc@hyc-alicloud:~/linux/線程dome$ ./test
運行主線程
等待失敗!
hyc@hyc-alicloud:~/linux/線程dome$

創建多線程演示

#include <iostream>
#include <string>
#include <pthread.h>
using namespace std;// 創建多線程void *routine(void *agrs)
{string name = static_cast<char *>(agrs);cout << "創建線程:" << name << endl;return nullptr;
}int main()
{for (int i = 0; i < 5; i++){pthread_t tid;char str[10];snprintf(str, sizeof(str), "%s%d", "thread-", i);pthread_create(&tid, nullptr, routine, (void *)str);}while (true){}
}hyc@hyc-alicloud:~/linux/多線程dome$ make
g++ -o test test.cc -l pthread
hyc@hyc-alicloud:~/linux/多線程dome$ ./test
創建線程:thread-1
創建線程:thread-4
創建線程:thread-4
創建線程:thread-4
創建線程:thread-4

? ? ? ? 此時,我們可以看見結果不太對,這是因為for循環的速度與新鍵線程的速度并不一致,導致開沒有開始創建對應的線程時,str里面的內容又被刷新了!

? ? ? ? 處理辦法:開辟獨立的空間,避免被覆蓋!

#include <iostream>
#include <string>
#include <vector>
#include <pthread.h>
using namespace std;// 創建多線程void *routine(void *agrs)
{string *name = static_cast<string *>(agrs);cout << "創建線程:" << *name << endl;return nullptr;
}int main()
{vector<pthread_t> arr;for (int i = 0; i < 5; i++){pthread_t tid;string *name = new string("thread-" + to_string(i));pthread_create(&tid, nullptr, routine, name);arr.push_back(tid);}for (int i = 0; i < 5; i++){int ret = pthread_join(arr[i], nullptr);if (ret == 0)cout << "等待成功" << endl;}
}hyc@hyc-alicloud:~/linux/多線程dome$ ./test
創建線程:thread-0
創建線程:thread-1
創建線程:thread-3
創建線程:thread-2
創建線程:thread-4
等待成功
等待成功
等待成功
等待成功
等待成功
hyc@hyc-alicloud:~/linux/多線程dome$ 

線程ID與進程地址空間布局

線程ID

hyc@hyc-alicloud:~$ ps -aLPID     LWP TTY          TIME CMD103519  103519 pts/3    00:00:00 test103519  103520 pts/3    00:00:04 test103519  103521 pts/3    00:00:05 test103519  103522 pts/3    00:00:04 test103519  103523 pts/3    00:00:04 test103519  103524 pts/3    00:00:04 test

? ? ? ? 首先,我們要區分LWP號與線程ID的區別。

????????LWP號是輕量級線程(LWP)的編號,但為了給用戶提供線程的概念,LWP號肯定不能提供給用戶,于是線程庫提供了標識號:線程ID!

? ? ? ? 那這個線程ID本質是什么東西呢?接著往下看!

進程地址空間分布

? ? ? ? Linux下的線程是由線程庫提供的,而庫是滿足EIF文件格式,動態庫會加載到物理內存空間中,然后再映射到需要的虛擬地址空間中共享區!

? ? ? ? 最后通過起始地址+偏移量的方式,就可以訪問到線程庫中的方法與數據了!

? ? ? ? 線程的概念是在pthread庫中被維護的!那這也意味著庫中一定有大量的被創建的線程!

? ? ? ? 庫一定會管理這些線程,如何管理?先描述,再組織!

描述:

????????庫中存在結構體,TCB用于描述線程對應屬性!

strcut TCB
{線程狀態線程ID線程獨立的棧結構線程棧的大小.....
}注:TCB中并沒有關于線程運行的屬性,如:優先級、時間片、上下文等等

組織:

? ? ? ? 通過數組進行組織!

? ? ? ? TCB分為3大部分:struct pthread、線程局部存儲、線程棧。其中每一個線程都必須有對應的線程棧!因為線程棧主要用于存儲代碼的臨時數據。注:主線程的棧空間并不在線程庫中!

? ? ? ? 線程ID:線程ID其實就是對應的TCB地址!

? ? ? ? 返回值:線程返回值,其實是寫入了struct pthread中的void* ret中,線程等待接口參數的變量之所以是void **,是為了拿到void *ret的數據!

? ? ? ? 線程等待:等待釋放資源,就是為了釋放TCB這個資源!

見一見線程ID:創建線程:thread-0
線程ID:140610093467200
線程ID:140610085074496
線程ID:140610076681792
線程ID:140610068289088
線程ID:140610059896384

? ? ? ? 而strcut pthread中之所有沒有關于線程執行的屬性,如:時間片、優先級。是因為線程的執行工作是交給了底層的系統調用clone!由clone去執行并返回結果!

函數:pthread_create(創建線程),其底層其實封裝了系統調用:clone(創建輕量級進程)#include <sched.h>int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...,* pid_t *parent_tid,void *tls, pid_t *child_tid */ );int clone(int (*fn)(void *),        // 1. 子進程執行的函數void *stack,              // 2. 子進程的棧指針int flags,                // 3. 核心控制標志(位掩碼)void *arg,                // 4. 傳遞給fn的參數...  /* 可選參數,順序固定 */pid_t *parent_tid,        // 5. 父進程中存儲子進程TID的地址void *tls,                // 6. 線程本地存儲(TLS)結構地址pid_t *child_tid);        // 7. 子進程中存儲自身TID的地址

????????所以,調用pthread_create方法會執行兩大步:

? ? ? ? 1.在庫中創建線程的控制管理塊,TCB

? ? ? ? 2.調用系統調用clone,在內核中創建輕量級進程,并傳入執行方法,讓其執行!

????????總結來說,用戶態管理線程的邏輯信息,內核態負責實際的調度執行,兩者通過系統調用協作,實現線程的創建與運行。

值得一提:Linux中線程(用戶級)與內核LWP是一對一的關系!

線程棧

? ? ? ? 首先,主線程的棧與子線程的棧是不一樣的!

? ? ? ? 主線程的棧大小不固定,可以向下增長!但子線程的棧是固定的,用完就完了,不會增長!

? ? ? ? 對于子線程的棧空間,原則上是線程私有的!但是其他線程想要訪問還是可以訪問的,沒有特殊的限制。

線程局部存儲

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;int num = 0;void *run1(void *args)
{while (1){cout << "修改num:" << num++ << endl;sleep(1);}
}void *run2(void *args)
{while (1){cout << "num:" << num << endl;sleep(1);}
}int main()
{pthread_t tid1, tid2;pthread_create(&tid1, nullptr, run1, nullptr);pthread_create(&tid2, nullptr, run2, nullptr);while (1){}
}hyc@hyc-alicloud:~/linux/線程局部存儲$ ./test
修改num:0
num:1
修改num:1
num:2
修改num:2
num:3
修改num:3
num:4
修改num:4
num:5
修改num:5
num:6
修改num:6
num:7
修改num:7
num:8
修改num:8

? ? ? ? 我們可以看到,兩個線程訪問了同一個全局變量,這也符合我們的預期。

? ? ? ? 下面來看看,線程局部存儲的效果。

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;// __thread聲明線程局部存儲變量
__thread int num = 0;void *run1(void *args)
{while (1){cout << "修改num:" << num++ << endl;sleep(1);}
}void *run2(void *args)
{while (1){cout << "num:" << num << endl;sleep(1);}
}int main()
{pthread_t tid1, tid2;pthread_create(&tid1, nullptr, run1, nullptr);pthread_create(&tid2, nullptr, run2, nullptr);while (1){}
}hyc@hyc-alicloud:~/linux/線程局部存儲$ ./test
修改num:0
num:0
修改num:1
num:0
修改num:2
num:0
修改num:3
num:0
修改num:4
num:0
修改num:5
num:0
修改num:6
num:0

? ? ? ? 由運行結果我們可以知道,線程局部存儲就是為每個線程創建該變量的獨立副本,不同線程之間變量互不干擾。只是名字都是一樣的(理解為類似寫時拷貝的效果)

? ? ? ? 作用:當我們想要使用全局變量,但又不想被其他線程干擾。這時就可以聲明線程局部存儲!

? ? ? ? 限制:線程局部存儲只能申明內置類型、部分指針

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

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

相關文章

計算機視覺與深度學習 | 具身智能研究綜述:從理論框架到未來圖景

具身智能研究綜述:從理論框架到未來圖景 文章目錄 具身智能研究綜述:從理論框架到未來圖景 一、定義與核心特征 二、關鍵技術體系 2.1 感知-運動融合技術 2.2 認知架構 2.3 強化學習進展 三、發展歷程與里程碑 3.1 理論奠基期(1990-2005) 3.2 技術探索期(2006-2015) 3.3 …

玩轉deepseek之自動出試卷可直接導出word

小伙伴們&#xff0c;最近有新同事入職&#xff0c;經理讓我出一個關于sqlserver相關的試卷&#xff0c;想著既然有deepseek&#xff0c;我們就偷懶下直接用deepseek給我們自動生成出來。打開deepseek官網&#xff0c;輸入提示詞&#xff1a;出一套SQL的試題要有基礎考察&#…

Flutter 語聊房項目 ----- 禮物特效播放

在語聊房項目中&#xff0c;禮物特效播放是一個常見的需求&#xff0c;通常包括動畫、聲音等多種媒體形式。為了處理不同的禮物類型&#xff0c;我們可以采用抽象的設計方法&#xff0c;使得系統易于擴展和維護。設計架構思路&#xff1a;抽象禮物特效接口&#xff1a;定義一個…

如何實現文件批量重命名自動化

在編程、設計、數據處理等工作中&#xff0c;腳本或軟件往往要求文件名符合特定格式。 批量重命名可快速將文件調整為所需命名規則&#xff0c;避免手動操作出錯。 它的體積不到300KB&#xff0c;解壓后直接運行&#xff0c;完全綠色無安裝。 界面清爽&#xff0c;操作直觀&a…

【數據結構——圖與鄰接矩陣】

引入 樹的遍歷方式可分為深搜和廣搜&#xff0c;這同樣適用于圖&#xff0c;不過有些地方會有出入。 樹的節點結構從根到葉子節點都是1&#xff1a;n,到葉子節點后就沒有了。而對于圖來說&#xff0c;如果到了最底下的節點&#xff0c;它可能除了連接已經記錄過的上層節點&am…

Quarkus - 超音速亞原子Java,開啟云原生應用新視界!

Quarkus - 超音速亞原子Java框架 Quarkus 是一個以云為中心、優先考慮&#xff08;Linux&#xff09;容器的框架&#xff0c;專為編寫 Java 應用而設計。它旨在幫助開發者更輕松地構建和部署大規模的容器化 Java 應用&#xff0c;采用了一系列現代開發理念和標準。 核心特點 …

如何查看GPU運行情況:使用 Conda 安裝 nvitop 新手指南

文章目錄 ?? 1. 為什么推薦使用 Conda 環境安裝 ?? 2. 安裝步驟 步驟 1: 安裝 Miniconda 或 Anaconda (如果你還沒有安裝的話) 步驟 2: 創建并激活一個專門的 Conda 環境 步驟 3: 在 Conda 環境中安裝 nvitop 步驟 4: 驗證安裝 ?? 3. 疑難解答 ?? 4. nvitop 的基本使用…

遙感機器學習專欄簡介

專欄定位與受眾本專欄聚焦「機器學習 遙感應用」的落地實踐&#xff0c;專為遙感相關專業大學生、剛入門的遙感工程師、機器學習愛好者打造。避開純理論堆砌&#xff0c;以「實驗課式實操」為核心&#xff0c;幫你解決 “懂理論但不會用代碼落地”“遙感數據處理與模型結合難”…

【更新至2024年】1996-2024年各省農業總產值數據(無缺失)

【更新至2024年】1996-2024年各省農業總產值數據&#xff08;無缺失&#xff09; 1、時間&#xff1a;1996-2024年 2、來源&#xff1a;國家統計局、各省年檢 3、指標&#xff1a;農業總產值 4、范圍&#xff1a;31省 5、缺失情況&#xff1a;無缺失 6、指標解釋&#xf…

大語言模型預訓練流程

大語言模型訓練流程 Pre-training → SFT → RLHF階段1&#xff1a;預訓練Pre-training 海量無標注文本數據訓練自監督學習機制學習語言基礎知識掌握語法、語義、常識形成語言表示能力 核心目標&#xff1a;建立模型的語言理解和文本生成基礎能力 階段2&#xff1a;監督微調Sup…

Zookeeper:分布式協調服務

一、概念ZooKeeper 是一個分布式的、開源的分布式應用程序協調服務&#xff0c;為分布式應用提供一致性、配置管理、命名服務、分布式同步和組服務等。可以把它想象成一個為分布式系統提供的“文件系統”“通知機制”&#xff0c;但它存儲的不是普通的文件&#xff0c;而是少量…

海盜王客戶端BMP紋理圖片解密

海盜王客戶端的紋理貼圖bmp文件有些是加密&#xff0c;很多人想解密并修改替換&#xff0c;現在給出解密的python代碼&#xff1a; import os import struct import copy from pathlib import Pathclass TexEncode:def __init__(self):self.MAGIC_BYTES bmp.x # 魔法字節標識…

《鏈式二叉樹常用操作全解析》

目錄 一.求鏈式二叉樹節點個數 二.求鏈式二叉樹葉子節點個數 三.求鏈式二叉樹第k層節點個數 四.求鏈式二叉樹的深度/高度 五.鏈式二叉樹查找值為x的節點 六.鏈式二叉樹的銷毀 七. 測試函數 八. 總結: 前言: 在學習鏈式二叉樹的常用操作之前 我們需要手動創建一個二叉樹 在…

YOLO11目標檢測運行推理簡約GUI界面

YOLO11推理簡約GUI界面使用方法&#xff1a;支持pt和onnx格式模型,并且自動檢測設備&#xff0c;選擇推理設備選擇推理圖片所在的文件夾 選擇推理后的結果保存地址選擇所需要的置信度閾值點擊開始推理&#xff0c;程序自動運行 并在下方實時顯示推理進度非常方便不用每次都改代…

集值優化問題:理論、應用與前沿進展

本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; 1. &#x1f4da; 集值優化問題概述 集值優化問題主要研究目標函數為…

提示工程架構師分享:如何用提示詞升級職業教育的實操案例教學?(萬字長文來襲,高能預警!!!)

引言&#xff1a;實操案例教學的“困境”&#xff0c;終于有了破局思路&#xff1f; 晚上10點&#xff0c;汽修專業的王強老師還在電腦前修改《汽車發動機異響故障排查案例》——這已經是他本周第四次調整方案了&#xff1a; 第一次授課時&#xff0c;學生反饋“案例太理想化&a…

「日拱一碼」087 機器學習——SPARROW

目錄 SPARROW 介紹 核心思想&#xff1a;稀疏掩碼訓練 與 Lottery Ticket Hypothesis (LTH) 的關系 代碼示例 代碼關鍵點解釋&#xff1a; 在機器學習領域&#xff0c;"SPARROW" 并不是一個像 Scikit-learn、TensorFlow 或 PyTorch 那樣廣為人知的通用框架或算法…

18、決策樹與集成學習 - 從單一智慧到群體決策

學習目標:理解決策樹的構建原理和分裂標準,掌握信息增益、基尼系數等概念,學會決策樹的剪枝方法,深入理解集成學習的思想,掌握隨機森林和梯度提升的基本原理。 > 從第17章到第18章:從概率模型到規則模型 在第17章中,我們學習了邏輯回歸——一個基于概率的線性分類器…

王道計算機組成原理 學習筆記

第一章計算機系統概述1.1計算機的發展歷程1.2計算機系統層次結構1.2.11.2.2 計算機硬件的基本組成1.2.2 各個硬件的工作原理1.2.3 計算機軟件1.2.4 計算機系統的層次結1.2.5 計算機系統的工作原理1.3計算機的性能指標第二章數據的表示和運算第三章存儲系統第四章指令系統第五章…

Oracle 筆記1 表空間及用戶

Oracle 筆記1 表空間及用戶1 安裝Oracle2 創建表空間3 創建表空間用戶1. 核心管理用戶2. 示例與工具用戶3. 系統與服務用戶4. 創建表空間用戶5. 修改表空間用戶特性OracleMySQL開發商Oracle 公司最初由 MySQL AB 開發&#xff0c;后被 Sun 收購&#xff0c;現屬 Oracle 公司數據…