linux網絡編程之單reactor模型(一)

????????Reactor 是一種事件驅動的設計模式(Event-Driven Pattern),主要用于處理高并發 I/O,特別適合網絡服務器場景。它通過一個多路復用機制監聽多個事件源(如 socket 文件描述符),并在事件就緒時將事件分發給對應的處理器(回調函數)執行。

六、單reactor單線程模型

1、核心思想

????????利用一個統一的事件分發中心(EventLoop),在單個線程中通過 I/O 多路復用機制(如 epoll)高效監聽和響應多個并發連接的 I/O 事件,將事件檢測與事件處理解耦,從而實現結構簡單、性能良好的并發 I/O 服務器。

2、核心組件

類名主要職責
Channel封裝一個 fd 的事件及回調,是 fd 與 epoll 的橋梁
EventLoop管理 epoll 及事件分發,是每個線程中的核心循環
TcpServer管理監聽 socket 和連接接入邏輯,創建 TcpConn
TcpConn封裝已建立的連接的讀寫處理、緩沖、生命周期等
1)Channel類-事件通道

Channel 是 一個 fd 與其事件處理邏輯之間的中介,負責:

  • 事件注冊:設置監聽哪些事件(如 EPOLLIN, EPOLLOUT

  • 事件觸發:內核通知時,調用用戶設置的回調函數

  • 與 EventLoop 協作:將事件注冊/修改到 epoll

.h

/* 負責在事件分發系統中起到“事件通道”的作用,連接底層的I/O多路復用機制(如epoll、select、poll)和具體的事件處理邏輯 */
class EventLoop;class Channel {
public:Channel(EventLoop& loop, int fd);~Channel();// 設置 fd 對應的感興趣事件(EPOLLIN/EPOLLOUT)void EnableReading();void EnableWriting();void DisableWriting();void DisableAll();// 用戶提供的讀寫事件回調void SetReadCallback(std::function<void()> cb);void SetWriteCallback(std::function<void()> cb);// 實際處理事件觸發,調用該函數判斷是否調用 read_cb_ 或 write_cb_void HandleEvent(uint32_t events);// 獲取 fdint GetFd() const;// 獲取事件uint32_t GetEvents() const;// 將當前 Channel 注冊到 epoll 或修改其狀態。void Update();private:int fd_;                          // 監聽的文件描述符bool added_ = false;              // 是否已添加到 epollEventLoop& loop_;                 // 所屬的事件循環uint32_t events_;                 // 當前監聽的事件類型(EPOLLIN/EPOLLOUT)std::function<void()> read_cb_;   // 讀事件回調std::function<void()> write_cb_;  // 寫事件回調
};
函數名功能調用時機
EnableReading()關注讀事件并更新 epoll 狀態監聽 socket / 連接 socket 可讀時
EnableWriting()關注寫事件(注冊 EPOLLOUT)Send() 數據寫不完時注冊
DisableWriting()注銷寫事件(避免忙等)寫完所有 buffer 后
HandleEvent(events)根據 epoll 返回事件觸發回調epoll_wait 返回后由 EventLoop 調用
Update()將本 Channel 注冊或修改到 epoll每次 Enable/Disable 事件后必須調用

.cpp

Channel::Channel(EventLoop& loop, int fd): fd_(fd), loop_(loop), events_(0) {}Channel::~Channel() {DisableAll();
}void Channel::EnableReading() {events_ |= EPOLLIN;Update();
}void Channel::EnableWriting() {events_ |= EPOLLOUT;Update();
}void Channel::DisableWriting() {events_ &= ~EPOLLOUT;Update();
}void Channel::DisableAll() {loop_.DelEvent(fd_);events_ = 0;
}void Channel::SetReadCallback(std::function<void()> cb) {read_cb_ = std::move(cb);
}void Channel::SetWriteCallback(std::function<void()> cb) {write_cb_ = std::move(cb);
}void Channel::HandleEvent(uint32_t events) {if ((events & EPOLLIN) && read_cb_) read_cb_();if ((events & EPOLLOUT) && write_cb_) write_cb_();
}int Channel::GetFd() const {return fd_;
}uint32_t Channel::GetEvents() const {return events_;
}void Channel::Update() {if (!added_) {loop_.AddEvent(fd_, events_, this);added_ = true;} else {loop_.ModEvent(fd_, events_, this);}
}
2)EventLoop類-事件循環

EventLoop 是 Reactor 的核心調度器,負責:

  • 管理所有 Channel 的事件監聽

  • 調用 epoll_wait 等待就緒事件

  • 分發事件并調用 Channel 的處理函數

.h

#define MAX_EVENTS 1024/* 負責事件的等待、分發和調度執行*/
class EventLoop
{
public:EventLoop() ;~EventLoop();void AddEvent(int fd, uint32_t events, void *ptr);void ModEvent(int fd, uint32_t events, void *ptr);void DelEvent(int fd);void Run();void stop();
private:int epfd_;bool running_;
};
函數名功能說明
Run()啟動事件循環持續調用 epoll_wait 并觸發回調
stop()停止事件循環設置 running_ 為 false
AddEvent(fd, events, ptr)添加一個 fd 到 epollfd 封裝在 Channel 內,由 TcpConn 創建
ModEvent(fd, events, ptr)修改 fd 的監聽事件典型于 Send() 或關閉寫事件
DelEvent(fd)從 epoll 中刪除 fd連接關閉、Channel 銷毀時

.cpp

EventLoop::EventLoop() : epfd_(::epoll_create1(0)), running_(true)
{if (epfd_ == -1){std::cerr << "epoll_create error: " << errno << std::endl;exit(EXIT_FAILURE);}
}EventLoop::~EventLoop()
{close(epfd_);
}void EventLoop::AddEvent(int fd, uint32_t events, void *ptr)
{epoll_event ev;ev.events = events;ev.data.ptr = ptr;if (::epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, &ev) == -1){std::cerr << "epoll_ctl add error: " << errno << std::endl;}
}void EventLoop::ModEvent(int fd, uint32_t events, void *ptr)
{epoll_event ev;ev.events = events;ev.data.ptr = ptr;if (::epoll_ctl(epfd_, EPOLL_CTL_MOD, fd, &ev) == -1){std::cerr << "epoll_ctl mod error: " << errno << std::endl;}
}void EventLoop::DelEvent(int fd)
{if (::epoll_ctl(epfd_, EPOLL_CTL_DEL, fd, nullptr) == -1){std::cerr << "epoll_ctl del error: " << errno << std::endl;}
}void EventLoop::Run()
{epoll_event events[MAX_EVENTS];while (running_){// 超時參數傳入TimerInstance()->WaitTime(),功能是確保 epoll_wait 最遲要在定時任務到期時返回,否則任務會延遲處理。int nfds = ::epoll_wait(epfd_, events, MAX_EVENTS, TimerInstance()->WaitTime());if (nfds == -1){if (errno == EINTR) // EINTR(系統中斷)時忽略重試,其他錯誤打印后直接返回。continue;std::cerr << "epoll_wait error: " << errno << std::endl;return;}// 遍歷就緒事件數組for (int i = 0; i < nfds; ++i){// 獲取就緒事件存儲在 data.ptr 的 Channel*Channel* ch = static_cast<Channel*>(events[i].data.ptr);// 觸發讀/寫等回調ch->HandleEvent(events[i].events);}// 處理定時器任務TimerInstance()->HandleTimeout();}
}void EventLoop::stop() {running_ = false;
}
3)TcpServer-連接管理

TcpServer 是服務端框架的頂層組織者,負責:

  • 創建監聽 socket:創建 socket、bind、listen

  • 創建Accept?Channel:負責接收新連接事件并注冊到 EventLoop

  • 創建新連接回調:接收到新連接后,通過回調交由用戶處理

.h

class TcpConn;
class EventLoop;class TcpServer {
public:// 新連接回調,供用戶處理新連接事件。using NewConnCallback = std::function<void(std::shared_ptr<TcpConn>)>;TcpServer(EventLoop& loop);~TcpServer();// 啟動 TCP 服務監聽,注冊新連接的回調void Start(uint16_t port, NewConnCallback cb);private:// 新連接建立時的處理邏輯,作為回調函數使用void HandleAccept();private:EventLoop& loop_;int listen_fd_;std::shared_ptr<Channel> accept_channel_; // 監聽 fd 的事件通道,負責讀事件注冊(新連接到來)NewConnCallback new_conn_cb_;             // 外部注入的新連接回調函數
};
函數名功能說明
Start(port, cb)初始化 socket,設置 Channel 和回調注冊 EPOLLIN 用于接收連接
HandleAccept()有新連接到來時被觸發accept() 然后調用回調構建 TcpConn
new_conn_cb_用戶設置的處理邏輯通常設置為 lambda 創建 TcpConn 實例

.cpp

TcpServer::TcpServer(EventLoop& loop): loop_(loop), listen_fd_(-1) {}TcpServer::~TcpServer() {if (listen_fd_ != -1) {accept_channel_->DisableAll();close(listen_fd_);}
}void TcpServer::Start(uint16_t port, NewConnCallback cb) {new_conn_cb_ = std::move(cb); // 保存回調函數// 創建 IPv4 TCP 套接字,非阻塞模式listen_fd_ = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);if (listen_fd_ == -1) {std::cerr << "socket error: " << errno << std::endl;return;}// 配置 SO_REUSEADDR(端口立即重用,避免“Address already in use”) 和 SO_REUSEPORT(多進程監聽同一端口,可用于多核負載均衡)int opt = 1;setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));// 綁定 socket 到指定端口地址sockaddr_in addr{};addr.sin_family = AF_INET;addr.sin_addr.s_addr = INADDR_ANY;addr.sin_port = htons(port);if (bind(listen_fd_, (sockaddr*)&addr, sizeof(addr)) == -1) {std::cerr << "bind error: " << errno << std::endl;close(listen_fd_);return;}// 開始監聽,監聽隊列長度為 SOMAXCONN(4096)if (listen(listen_fd_, SOMAXCONN) == -1) {std::cerr << "listen error: " << errno << std::endl;close(listen_fd_);return;}// 創建 Channel 對象,監聽 listen_fd_ 上的事件,會注冊到 epoll 中accept_channel_ = std::make_shared<Channel>(loop_, listen_fd_);// 設置讀事件回調函數:即新連接到來時應調用的邏輯函數accept_channel_->SetReadCallback([this]() { HandleAccept(); });// 啟動監聽accept_channel_->EnableReading();std::cout << "Server listening on port " << port << std::endl;
}void TcpServer::HandleAccept() {sockaddr_in client_addr{};socklen_t len = sizeof(client_addr);// 接受新連接,返回新的客戶端 fd,設置非阻塞int conn_fd = accept4(listen_fd_, (sockaddr*)&client_addr, &len, SOCK_NONBLOCK);if (conn_fd == -1) return;// 創建新的連接對象(TcpConn)管理該客戶端 fdauto conn = std::make_shared<TcpConn>(conn_fd, loop_);// 調用用戶邏輯處理這個連接if (new_conn_cb_) new_conn_cb_(conn);
}
4)TcpConn-與客戶端通信

TcpConn 封裝了每個 TCP 連接的生命周期、讀寫事件、緩沖區管理等。

.h

/**聲明 TcpConn 類,同時繼承 enable_shared_from_this,方便在回調中安全獲取 shared_ptr<TcpConn> 自己的智能指針。*/
class TcpConn : public std::enable_shared_from_this<TcpConn> {
public:// 聲明回調函數using ReadCallback = std::function<void()>;using CloseCallback = std::function<void()>;TcpConn(int fd, EventLoop& loop);~TcpConn();// 設置回調函數void SetReadCallback(ReadCallback cb);void SetCloseCallback(CloseCallback cb);// 異步發送數據int Send(const char* data, size_t size);// 獲取當前接收緩沖區內的全部數據std::string GetAllData();private:// 內部事件處理函數:讀取、寫入、關閉連接void HandleRead();void HandleWrite();void Close();private:int fd_;bool closed_;EventLoop& loop_;MessageBuffer input_buffer_;       // 讀緩沖區std::string output_buffer_{};        // 寫緩沖區std::shared_ptr<Channel> channel_; // 封裝 fd 的 epoll 管理類ReadCallback read_cb_;             // 外部注入的回調函數CloseCallback close_cb_;
};
函數名功能調用說明
HandleRead()可讀事件觸發時從 fd 讀數據到 input_buffer_然后觸發 read_cb_
HandleWrite()可寫事件觸發時將 output_buffer_ 中數據寫出寫完后注銷 EPOLLOUT
Send(data)異步發送數據若 fd 可寫則立即寫,否則加入 buffer
GetAllData()獲取 input_buffer_ 所有數據可在 onMessage 中使用
Close()主動關閉連接注銷事件、關閉 fd、觸發 close_cb_

.cpp

TcpConn::TcpConn(int fd, EventLoop& loop): fd_(fd), closed_(false), loop_(loop) {// // 設置 socket 為非阻塞模式// int flags = fcntl(fd, F_GETFL, 0);// fcntl(fd, F_SETFL, flags | O_NONBLOCK);// 創建 Channel 并設置讀寫回調,注冊 EPOLLIN 監聽可讀事件。channel_ = std::make_shared<Channel>(loop_, fd);channel_->SetReadCallback([this]() { HandleRead(); });channel_->SetWriteCallback([this]() { HandleWrite(); });channel_->EnableReading();
}TcpConn::~TcpConn() {Close();
}void TcpConn::SetReadCallback(ReadCallback cb) {read_cb_ = std::move(cb);
}void TcpConn::SetCloseCallback(CloseCallback cb) {close_cb_ = std::move(cb);
}int TcpConn::Send(const char* data, size_t size) {// 若連接已關閉或無數據,直接返回。if (closed_ || data == nullptr || size == 0) return -1;// 如果寫緩沖區中已有未發送的數據,追加數據并監聽寫事件。if (!output_buffer_.empty()) {output_buffer_.append(data, size);channel_->EnableWriting();return size;}// 直接發送(零拷貝),MSG_NOSIGNAL:防止對方關閉連接時觸發 SIGPIPE 信號(使得進程崩潰)。int n = send(fd_, data, size, MSG_NOSIGNAL);if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { // 非阻塞 socket 下,內核發送緩沖區可能暫時滿// 將數據緩存到用戶態 output_buffer_, 監聽寫事件output_buffer_.append(data, size);channel_->EnableWriting();} else if (n > 0 && n < static_cast<int>(size)) {   // 有部分數據被寫入 socket(如發送了 n 字節,小于總長度 size)// 剩下的數據未能寫完,需要緩存到 output_buffer_,并監聽寫事件output_buffer_.append(data + n, size - n);channel_->EnableWriting();} else if (n < 0) {Close();}return n;
}std::string TcpConn::GetAllData() {auto data = input_buffer_.GetAllData();if (data.first != nullptr) {// 獲取讀緩沖區全部有效數據std::string result(reinterpret_cast<char*>(data.first), data.second);// 標記已讀的數據input_buffer_.ReadCompleted(data.second);return result;}return "";
}void TcpConn::HandleRead() {int err = 0;// 調用 MessageBuffer::Recv() 使用 readv() 讀取數據,讀取數據到緩沖區中。int n = input_buffer_.Recv(fd_, &err);if (n > 0 && read_cb_) {read_cb_(); // 觸發讀取回調邏輯} else if (n == 0 || (n < 0 && err != EAGAIN && err != EWOULDBLOCK)) { // 連接關閉或錯誤則關閉連接。Close();}
}void TcpConn::HandleWrite() {// 如果寫緩沖區為空,則取消監聽寫事件。if (output_buffer_.empty()) {channel_->DisableWriting();return;}// 緩沖區不為空,調用 send() 發送數據,發送成功則刪除發送緩沖區中的數據。int n = send(fd_, output_buffer_.data(), output_buffer_.size(), MSG_NOSIGNAL);if (n > 0) {output_buffer_.erase(0, n);if (output_buffer_.empty()) {channel_->DisableWriting();}} else if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { // 非阻塞錯誤以外的寫失敗則關閉連接Close();}
}void TcpConn::Close() {if (closed_) return;closed_ = true;channel_->DisableAll();close(fd_);if (close_cb_) close_cb_();
}

3、完整調用流程

啟動階段:

  1. TcpServer::Start(port, cb)

    • 創建 listen_fd,設為非阻塞

    • 創建 accept_channel_ 封裝 listen_fd

    • 設置其 read_cbTcpServer::HandleAccept

    • 將其注冊到 EventLoop 中(AddEvent()


有連接到來:

  1. 內核通知 listen_fd 可讀 → Channel::HandleEvent()read_cb_TcpServer::HandleAccept()

    • 調用 accept(),獲得 conn_fd

    • 設置為非阻塞

    • 構建 TcpConn 對象,封裝連接的生命周期

    • 創建該連接的 Channel,并注冊其 read_cb_TcpConn::HandleRead


數據收發流程:

  1. conn_fd 可讀 → epoll 通知 → EventLoop 觸發 TcpConn::HandleRead()

    • 從 socket 讀入數據到 input_buffer_

    • 調用 read_cb_ 讓上層應用邏輯處理數據

  2. TcpConn::Send()

    • 若當前 fd 可寫,則直接發送

    • 否則寫入 output_buffer_,并注冊 EPOLLOUT 事件

    • channel_->SetWriteCallback(...) 設定寫回調

  3. conn_fd 可寫 → TcpConn::HandleWrite()

    • output_buffer_ 中寫出數據

    • 若 buffer 為空,注銷寫事件,調用 writeCompleteCallback

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

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

相關文章

瀏覽器重繪與重排

深入解析瀏覽器渲染&#xff1a;重排(Reflow)與重繪(Repaint)的性能陷阱與優化策略作為一名前端開發者&#xff0c;你是否遇到過界面突然卡頓、滾動時頁面抖動或輸入框響應遲鈍&#xff1f;這些常見性能問題背后&#xff0c;往往是重排與重繪在作祟。本文將深入剖析瀏覽器渲染機…

day049-初識Ansible與常用模塊

文章目錄0. 老男孩思想-人脈的本質1. Ansible1.1 密鑰認證1.2 安裝ansible1.3 添加ansible配置文件1.4 配置主機清單文件&#xff08;Inventory&#xff09;1.5 測試1.6 ansible的模塊思想1.7 command模塊1.8 需求&#xff1a;每臺服務器的密碼都不同&#xff0c;怎么批量執行業…

力扣網編程134題:加油站(雙指針)

一. 簡介 前面兩篇文章使用暴力解法&#xff0c;或者貪心算法解決了力扣網的加油站問題&#xff0c;文章如下&#xff1a; 力扣網編程150題&#xff1a;加油站&#xff08;暴力解法&#xff09;-CSDN博客 力扣網編程150題&#xff1a;加油站&#xff08;貪心解法&#xff09…

XPath 語法【Web 自動化-定位方法】

&#x1f9ed; XPath 語法簡介&#xff08;Web 自動化核心定位手段&#xff09;一、XPath 是什么&#xff1f;XPath&#xff08;XML Path Language&#xff09;是用于在 XML/HTML 文檔中定位節點的語言&#xff0c;由 W3C 標準定義。瀏覽器支持的是 XPath 1.0。應用場景廣泛&am…

記一次 Linux 安裝 docker-compose

一.下載 1.手動下載 下載地址&#xff1a;https://github.com/docker/compose/releases 下載后&#xff0c;放在/usr/local/bin/目錄下&#xff0c;命名為&#xff1a;docker-compose 2.命令下載 sudo curl -L "https://github.com/docker/compose/releases/download/…

Go語言WebSocket編程:從零打造實時通信利器

1. WebSocket的魅力&#xff1a;為什么它這么火&#xff1f;WebSocket&#xff0c;簡單來說&#xff0c;就是一種在單條TCP連接上實現全雙工通信的神器。相比HTTP的請求-響應模式&#xff0c;它像是一條隨時暢通的電話線&#xff0c;客戶端和服務器可以隨時“喊話”&#xff0c…

速學 RocketMQ

目錄 本地啟動&測試&可視化 核心概念 集群 主從 集群 Dledger 集群 總結 客戶端消息確認機制 廣播模式 消息過濾機制 順序消息機制 延遲消息與批量消息 事務消息機制 ACL權限控制體系 RocketMQ客戶端注意事項 消息的 ID、Key、Tag 最佳實踐 消費者端…

【個人思考】不點菜的美學:Omakase 的信任、四季與食藝

本文原創作者:姚瑞南 AI-agent 大模型運營專家/音樂人/野生穿搭model,先后任職于美團、獵聘等中大廠AI訓練專家和智能運營專家崗;多年人工智能行業智能產品運營及大模型落地經驗,擁有AI外呼方向國家專利與PMP項目管理證書。(轉載需經授權) 目錄 ?? 什么是 Omakase?…

vivo Pulsar 萬億級消息處理實踐(3)-KoP指標異常修復

作者&#xff1a;vivo 互聯網大數據團隊- Chen Jianbo 本文是《vivo Pulsar萬億級消息處理實踐》系列文章第3篇。 Pulsar是Apache基金會的開源分布式流處理平臺和消息中間件&#xff0c;它實現了Kafka的協議&#xff0c;可以讓使用Kafka API的應用直接遷移至Pulsar&#xff0c;…

Marin說PCB之Allegro高亮BOM器件技巧詳解

一&#xff0c;首先在原理圖輸出BOM的時候&#xff0c;只需要勾選器件的位號這個選項即可&#xff0c;具體操作如下所示&#xff1a;二&#xff0c;輸出BOM完成后&#xff0c;打開表格選擇我們器件的位號那列即可&#xff0c;然后復制到我們的TEXT文本中。三&#xff0c;接著就…

數據結構與算法——從遞歸入手一維動態規劃【2】

前言&#xff1a; 記錄一下對左程云系列算法課程--算法講解066【必備】的剩余習題的學習。本文主要簡單記錄個人學習心得和提供C版本代碼。如需要題目的細致講解&#xff0c;請前往原視頻。 涉及內容&#xff1a; 動態規劃、三指針、 參考視頻&#xff1a; 左程云--算法講…

【理念●體系】Windows AI 開發環境搭建實錄:六層架構的逐步實現與路徑治理指南

【理念●體系】從零打造 Windows WSL Docker Anaconda PyCharm 的 AI 全鏈路開發體系-CSDN博客 Windows AI 開發環境搭建實錄&#xff1a;六層架構的逐步實現與路徑治理指南 ——理念落地篇&#xff0c;從路徑規劃到系統治理&#xff0c;打造結構化可復現的 AI 開發環境 AI…

5G標準學習筆記15 --CSI-RS測量

5G標準學習筆記15 --CSI-RS測量 前言 前面講了&#xff0c;在5GNR中&#xff0c;CSI-RS 是支持信道狀態評估、波束管理和無線資源管理&#xff08;RRM&#xff09;的關鍵參考信號。下面孬孬基于3GPP TS 38.331中的內容&#xff0c;詳細定義了基于 CSI-RS 的測量程序&#xff0c…

第P28:阿爾茨海默病診斷(優化特征選擇版)

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 一、進階說明 針對于特征對模型結果的影響我們做了特征分析 特征選擇 1. SelectFromModel 工作原理&#xff1a;基于模型的特征選擇方法&#xff0c;使用…

AI的歐幾里得要素時刻:從語言模型到可計算思維

引言 人工智能正在經歷一個關鍵的轉折點。就像歐幾里得的《幾何原本》為數學奠定了公理化基礎一樣&#xff0c;AI也正在尋找自己的"要素時刻"——一個能夠將當前的語言模型能力轉化為真正可計算、可驗證思考的轉變。 最近發表的論文《AI’s Euclid’s Elements Momen…

番外-linux系統運行.net framework 4.0的項目

基礎環境&#xff1a;linux系統&#xff0c;.net framework 4.0&#xff0c;npgsql 2.2.5.0 &#xff08;版本不同&#xff0c;構建可能失敗&#xff09; 方法背景&#xff1a;linux不支持運行.net framework 4.0&#xff0c;高版本mono不支持npgsql 2.x 主要使用&#xff1a…

國內AI訓練都有哪些企業?:技術深耕與場景實踐

國內AI訓練都有哪些企業&#xff1f;當人工智能從實驗室走向產業一線&#xff0c;AI 訓練就像為智能系統 “施肥澆水” 的關鍵環節&#xff0c;讓技術根系在各行業土壤里扎得更深。國內一批 AI 訓練企業正各展所長&#xff0c;有的專攻技術優化&#xff0c;有的深耕場景應用。它…

微算法科技基于格密碼的量子加密技術,融入LSQb算法的信息隱藏與傳輸過程中,實現抗量子攻擊策略強化

隨著量子計算技術的發展&#xff0c;傳統加密算法面臨被量子計算機破解的風險&#xff0c;LSQb 算法也需考慮應對未來可能的量子攻擊。微算法科技基于格密碼的量子加密技術&#xff0c;融入LSQb算法的信息隱藏與傳輸過程中&#xff0c;實現抗量子攻擊策略強化。格密碼在面對量子…

xAI發布Grok4+代碼神器Grok4 Code,教你如何在國內升級訂閱SuperGrok并使用到Grok4教程

就在今天&#xff0c;馬斯克旗下xAI發布了其最新的旗艦AI模型Grok4&#xff0c;并同步推出專為開發者打造的編程利器 Grok 4 Code&#xff0c;還推出了一項全新的AI訂閱計劃——每月300美元的SuperGrokHeavy。 那最新發布的Grok4以及有哪些特性呢&#xff1f;以及如何才能使用…

Rust 變量遮蔽(Variable Shadowing)

在 Rust 中&#xff0c;變量遮蔽&#xff08;Variable Shadowing&#xff09; 是一種在同一作用域內重新聲明同名變量的特性。它允許你創建一個新變量覆蓋之前的同名變量&#xff0c;新變量與舊變量類型可以不同&#xff0c;且舊變量會被完全隱藏。核心特點允許同名變量重復聲明…