跨越通信障礙:深入了解ZeroMQ的魅力

在復雜的分布式系統開發中,進程間通信就像一座橋梁,連接著各個獨立運行的進程,讓它們能夠協同工作。然而,傳統的通信方式往往伴隨著復雜的設置、高昂的性能開銷以及有限的靈活性,成為了開發者們前進道路上的 “絆腳石”。

今天,我們將一同探索一款被譽為分布式通信領域 “神器” 的工具 ——ZeroMQ,它以其獨特的設計理念和強大的功能,打破了這些通信障礙,為我們開啟了高效、靈活的進程間通信新世界。無論你是初涉分布式開發的新手,還是經驗豐富的技術專家,相信在深入了解 ZeroMQ 的過程中,都會被它的魅力所折服。

一、什么是ZeroMQ

1.1 ZeroMQ概述

ZeroMQ,也寫作 ?MQ、0MQ 或 zmq,是一個高性能的異步消息隊列庫 ,旨在用于分布式或并發應用程序。它提供了一個消息隊列的抽象,允許不同的計算機和進程之間進行消息傳遞,而無需關心底層網絡細節。與傳統的消息隊列服務器不同,ZeroMQ 是一個嵌入到應用程序中的庫,提供了多種消息傳遞模式,如請求 - 應答、發布 - 訂閱、推 - 拉等。憑借這些特性,使用 ZeroMQ 可以讓編寫高性能網絡應用程序變得極為簡單和有趣。接下來,讓我們深入了解一下 ZeroMQ 的特性。

1.2 ZeroMQ特性

(1)高性能設計

ZeroMQ 的高性能得益于其精心設計的內部機制。在其內部,無鎖隊列模型的應用是一大亮點。以跨線程間的數據交換通道 pipe 為例,它采用無鎖的隊列算法 CAS 。在傳統的多線程編程中,線程之間的同步往往依賴于鎖機制,這會帶來一定的性能開銷,比如線程上下文的切換、鎖的爭用等。而 CAS 算法避免了這些問題,它通過比較和交換操作來實現數據的原子性更新,使得在多線程環境下,數據交換能夠高效進行。在 pipe 的兩端注冊有異步事件,當有消息讀寫操作時,這些異步事件會自動觸發,進一步提高了數據處理的效率。

批量處理算法也是提升性能的關鍵。傳統的消息處理方式,每次消息的發送和接收都需要進行系統調用,這對于大量消息的處理來說,系統開銷是巨大的。ZeroMQ 則對批量消息進行了優化,它可以將多個消息集中起來進行處理,減少了系統調用的次數,從而顯著提高了消息處理的效率。

此外,ZeroMQ 還充分利用多核 CPU 的優勢,采用多核線程綁定技術。在傳統的多線程并發模式下,多個線程共享 CPU 資源,頻繁的 CPU 切換會帶來額外的開銷。而 ZeroMQ 將每個工作者線程綁定到特定的 CPU 核心上,避免了多線程之間的 CPU 切換開銷,使得每個核心都能充分發揮其計算能力,大大提升了系統的整體性能。

ZeroMQ 支持多種通信協議,包括進程內(inproc)、進程間(ipc)、TCP 等。這些協議為不同場景下的通信提供了選擇,比如進程內通信適用于同一進程內不同線程之間的快速數據交換,它的速度極快,因為不需要經過網絡等外部傳輸;而 TCP 協議則適用于不同主機之間的通信,具有良好的通用性和穩定性。不同的協議在不同的場景下都能發揮出最佳的性能,滿足了多樣化的應用需求。

(2)豐富的通信模式

請求 - 應答(REQ - REP)模式:這種模式類似于傳統的客戶端 - 服務器模型。在實際應用中,比如一個分布式系統中的文件查詢服務,客戶端(REQ)向服務器(REP)發送文件查詢請求,服務器接收到請求后,在本地文件系統中進行查詢,然后將查詢結果返回給客戶端。這種模式的特點是嚴格的同步性,客戶端發送請求后必須等待服務器的應答,服務器也必須在接收請求后再發送應答。在一個數據庫查詢場景中,客戶端向數據庫服務器發送查詢語句,服務器執行查詢后返回結果集,這就是典型的請求 - 應答模式。

發布 - 訂閱(PUB - SUB)模式:發布者(PUB)將消息發布到特定的主題,訂閱者(SUB)可以根據自己的興趣訂閱一個或多個主題。當發布者發布消息時,只有訂閱了相應主題的訂閱者才能接收到消息。在股票市場行情推送系統中,行情數據發布者會不斷發布股票的實時價格、成交量等信息,各個投資者的客戶端作為訂閱者,可以根據自己關注的股票代碼訂閱相應的行情數據。這種模式非常適合需要進行消息廣播和分發的場景,能夠實現一對多的數據傳輸。

推送 - 拉取(PUSH - PULL)模式:推送者(PUSH)將消息推送給多個拉取者(PULL),常用于任務分發和負載均衡。在一個分布式計算集群中,任務調度中心(PUSH)可以將計算任務分發給各個計算節點(PULL),各個計算節點并行處理任務,提高計算效率。這種模式下,消息的發送和接收是異步的,推送者不需要等待拉取者的響應,可以持續發送消息,從而實現高效的任務分發和數據傳輸。

(3)跨平臺與多語言支持

ZeroMQ 具有出色的跨平臺能力,它可以在 Linux、Windows、macOS 等多種主流操作系統上運行。這使得開發人員在不同的操作系統環境下都可以使用 ZeroMQ 來構建分布式應用,無需擔心因操作系統差異而帶來的兼容性問題。在一個跨平臺的分布式數據采集系統中,數據采集節點可能運行在不同的操作系統上,有的是 Linux 系統用于高效的數據處理,有的是 Windows 系統用于與特定的設備進行交互,而 ZeroMQ 能夠在這些不同系統的節點之間實現穩定的通信。

同時,ZeroMQ 支持多種編程語言,如 C++、Java、Python、Go 等。這意味著不同語言編寫的應用程序之間可以通過 ZeroMQ 進行通信,極大地提高了系統的靈活性和可擴展性。在一個大型的企業級應用中,后端的核心業務邏輯可能使用 C++ 編寫以追求高性能,而前端的用戶界面可能使用 Python 結合相關的 Web 框架進行開發,中間的數據傳輸和交互就可以借助 ZeroMQ 來實現。不同語言的開發者可以根據自己的技術棧和項目需求選擇合適的編程語言進行開發,而不用擔心通信問題,這為分布式系統的開發帶來了極大的便利 。

二、ZeroMQ 在C++中的使用步驟

2.1安裝 ZeroMQ 庫

在使用 ZeroMQ 之前,首先需要將其安裝到開發環境中。以 Linux 系統為例,使用包管理器進行安裝是最為便捷的方式,例如在基于 Debian 或 Ubuntu 的系統上,可以在終端中輸入以下命令:

sudo apt - get install libzmq3 - dev

這條命令會自動從軟件源中下載 ZeroMQ 庫及其相關的開發文件,并完成安裝過程。對于基于 Red Hat 或 CentOS 的系統,相應的安裝命令則是:

sudo yum install libzmq3 - devel

除了使用包管理器安裝,還可以從 ZeroMQ 的官方網站(http://zeromq.org)下載源代碼進行編譯安裝 。這種方式適用于對庫的版本有特定要求,或者軟件源中提供的版本不符合需求的情況。下載完成后,解壓源代碼壓縮包,進入解壓后的目錄,依次執行以下命令:

./configure
make
sudo make install

./configure命令用于檢查系統環境并生成 Makefile,make命令根據 Makefile 進行編譯,最后的sudo make install則將編譯好的庫文件和頭文件安裝到系統指定目錄中。在 Windows 系統下,同樣可以從官方網站獲取預編譯的二進制文件,然后將其添加到項目的庫路徑和包含路徑中,以便在項目中使用 ZeroMQ 庫。

2.2創建上下文和套接字

在 C++ 代碼中,使用 ZeroMQ 的第一步是創建上下文(Context)。上下文是 ZeroMQ 的核心概念之一,它是一個管理套接字、線程和 I/O 資源的對象,每個 ZeroMQ 應用程序都需要至少一個上下文。創建上下文非常簡單,使用zmq::context_t類即可,示例代碼如下:

#include <zmq.hpp>
int main() {// 創建一個ZeroMQ上下文,參數1表示上下文的I/O線程數,一般1個線程即可滿足大多數情況zmq::context_t context(1); // 后續代碼...return 0;
}

創建套接字(Socket)是接下來的關鍵步驟。套接字是 ZeroMQ 中用于發送和接收消息的基本對象,不同類型的套接字對應不同的通信模式。使用zmq::socket_t類來創建套接字,例如創建一個用于請求 - 應答模式的套接字:

// 創建一個REQ類型的套接字,用于請求-應答模式,客戶端使用
zmq::socket_t socket(context, zmq::socket_type::req);

如果要創建用于發布 - 訂閱模式的套接字,則可以這樣寫:

// 創建一個PUB類型的套接字,用于發布-訂閱模式,發布者使用
zmq::socket_t socket(context, zmq::socket_type::pub);

通過這種方式,根據不同的應用場景和通信需求,靈活選擇合適的套接字類型。

2.3綁定與連接

在服務器端,需要將套接字綁定(Bind)到一個特定的地址和端口,以便接收來自客戶端的連接和消息。以 TCP 協議為例,綁定的示例代碼如下:

// 將套接字綁定到TCP地址"tcp://*:5555",*表示綁定到所有可用的網絡接口
socket.bind("tcp://*:5555");

在上述代碼中,tcp://表示使用 TCP 協議,*表示服務器將監聽所有可用的網絡接口,5555是指定的端口號。通過綁定操作,服務器端的套接字就準備好接收客戶端的連接請求了。

對于客戶端來說,需要使用connect方法連接(Connect)到服務器的地址和端口。示例代碼如下:

// 連接到服務器的地址"tcp://localhost:5555",localhost表示本地主機
socket.connect("tcp://localhost:5555");

這里客戶端嘗試連接到本地主機(localhost)上的 5555 端口,如果服務器運行在其他主機上,只需將localhost替換為服務器的實際 IP 地址即可。連接成功后,客戶端和服務器之間就建立了通信鏈路,可以進行消息的發送和接收了。

2.4消息的發送與接收

在 ZeroMQ 中,發送消息使用send函數,接收消息使用recv函數。以發送一個簡單的字符串消息為例,代碼如下:

std::string message = "Hello, ZeroMQ!";
// 創建一個zmq::message_t對象,大小為消息的長度
zmq::message_t request(message.size()); 
// 將消息內容復制到zmq::message_t對象中
memcpy(request.data(), message.data(), message.size()); 
// 發送消息,zmq::send_flags::none表示使用默認的發送標志
socket.send(request, zmq::send_flags::none);

在接收消息時,同樣需要創建一個zmq::message_t對象來存儲接收到的消息,然后使用recv函數接收消息:

zmq::message_t reply;
// 接收消息,zmq::recv_flags::none表示使用默認的接收標志
socket.recv(reply, zmq::recv_flags::none); 
// 將接收到的消息轉換為字符串
std::string replyMessage(static_cast<char*>(reply.data()), reply.size()); 
std::cout << "Received reply: " << replyMessage << std::endl;

ZeroMQ 還支持非阻塞的發送和接收操作。在非阻塞模式下,send和recv函數不會等待操作完成,而是立即返回,通過返回值可以判斷操作是否成功。要使用非阻塞模式,需要在發送或接收時設置ZMQ_DONTWAIT標志。例如,非阻塞發送的代碼如下:

zmq::send_result_t result = socket.send(request, zmq::send_flags::dontwait);
if (!result) {// 處理發送失敗的情況,例如檢查errno獲取錯誤原因std::cerr << "Send failed: " << zmq_errno() << std::endl; 
}

非阻塞接收的代碼類似:

zmq::recv_result_t result = socket.recv(reply, zmq::recv_flags::dontwait);
if (!result) {// 處理接收失敗的情況std::cerr << "Recv failed: " << zmq_errno() << std::endl; 
}

通過這種方式,可以根據實際需求靈活選擇阻塞或非阻塞的消息發送和接收方式,以滿足不同場景下的性能和功能要求。

三、ZeroMQ通信模式實戰

3.1請求 - 應答模式

請求 - 應答模式是最常見的通信模式之一,常用于客戶端與服務器之間的交互。在這種模式下,客戶端(REQ)向服務器(REP)發送請求,服務器接收請求并處理后返回應答。下面是一個簡單的 C++ 代碼示例:

// 服務器端代碼
#include <zmq.hpp>
#include <iostream>int main() {// 創建ZeroMQ上下文zmq::context_t context(1); // 創建一個REP類型的套接字,用于接收請求并發送應答zmq::socket_t socket(context, zmq::socket_type::rep); // 將套接字綁定到地址"tcp://*:5555",*表示綁定到所有可用網絡接口socket.bind("tcp://*:5555"); while (true) {// 接收客戶端的請求zmq::message_t request; socket.recv(request, zmq::recv_flags::none); std::string request_str(static_cast<char*>(request.data()), request.size()); std::cout << "Received request: " << request_str << std::endl;// 處理請求后,發送應答std::string reply_str = "Reply to " + request_str; zmq::message_t reply(reply_str.size()); memcpy(reply.data(), reply_str.data(), reply_str.size()); socket.send(reply, zmq::send_flags::none); }return 0;
}
// 客戶端代碼
#include <zmq.hpp>
#include <iostream>int main() {// 創建ZeroMQ上下文zmq::context_t context(1); // 創建一個REQ類型的套接字,用于發送請求并接收應答zmq::socket_t socket(context, zmq::socket_type::req); // 連接到服務器的地址"tcp://localhost:5555"socket.connect("tcp://localhost:5555"); for (int i = 0; i < 5; ++i) {// 發送請求std::string request_str = "Request " + std::to_string(i); zmq::message_t request(request_str.size()); memcpy(request.data(), request_str.data(), request_str.size()); socket.send(request, zmq::send_flags::none); // 接收服務器的應答zmq::message_t reply; socket.recv(reply, zmq::recv_flags::none); std::string reply_str(static_cast<char*>(reply.data()), reply.size()); std::cout << "Received reply: " << reply_str << std::endl;}return 0;
}

在上述代碼中,服務器端創建了一個REP類型的套接字并綁定到tcp://*:5555地址,然后進入一個無限循環,不斷接收客戶端的請求并發送應答。客戶端創建了一個REQ類型的套接字并連接到服務器地址,通過循環發送 5 次請求,并接收服務器的應答。

3.2發布 - 訂閱模式

發布 - 訂閱模式用于消息的廣播和分發,發布者(PUB)將消息發布到特定的主題,訂閱者(SUB)可以訂閱感興趣的主題并接收相應的消息。下面是一個示例:

// 發布者代碼
#include <zmq.hpp>
#include <iostream>
#include <string>
#include <sstream>int main() {// 創建ZeroMQ上下文zmq::context_t context(1); // 創建一個PUB類型的套接字,用于發布消息zmq::socket_t socket(context, zmq::socket_type::pub); // 將套接字綁定到地址"tcp://*:5556"socket.bind("tcp://*:5556"); while (true) {// 生成消息內容,這里以時間作為消息內容auto now = std::chrono::system_clock::now();auto in_time_t = std::chrono::system_clock::to_time_t(now);std::stringstream ss;ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %H:%M:%S");std::string message = ss.str();// 發布消息,這里假設主題為"time"std::string topic = "time"; zmq::message_t topic_msg(topic.size()); memcpy(topic_msg.data(), topic.data(), topic.size()); socket.send(topic_msg, zmq::send_flags::sndmore); zmq::message_t msg(message.size()); memcpy(msg.data(), message.data(), message.size()); socket.send(msg, zmq::send_flags::none); std::cout << "Published message: " << message << " on topic: " << topic << std::endl;// 每2秒發布一次消息std::this_thread::sleep_for(std::chrono::seconds(2)); }return 0;
}
// 訂閱者代碼
#include <zmq.hpp>
#include <iostream>int main() {// 創建ZeroMQ上下文zmq::context_t context(1); // 創建一個SUB類型的套接字,用于訂閱消息zmq::socket_t socket(context, zmq::socket_type::sub); // 連接到發布者的地址"tcp://localhost:5556"socket.connect("tcp://localhost:5556"); // 訂閱主題"time"std::string topic = "time"; socket.setsockopt(ZMQ_SUBSCRIBE, topic.data(), topic.size()); while (true) {// 接收消息,先接收主題,再接收消息內容zmq::message_t topic_msg; socket.recv(topic_msg, zmq::recv_flags::none); std::string topic_str(static_cast<char*>(topic_msg.data()), topic_msg.size()); zmq::message_t msg; socket.recv(msg, zmq::recv_flags::none); std::string message(static_cast<char*>(msg.data()), msg.size()); std::cout << "Received message on topic: " << topic_str << ", message: " << message << std::endl;}return 0;
}

在這個示例中,發布者每隔 2 秒發布當前時間作為消息,消息主題為 "time"。訂閱者訂閱了 "time" 主題,接收到消息后打印出主題和消息內容。

3.3推送 - 拉取模式

推送 - 拉取模式常用于任務分發和負載均衡,推送者(PUSH)將消息推送給多個拉取者(PULL)。下面是一個示例:

// 推送端代碼
#include <zmq.hpp>
#include <iostream>int main() {// 創建ZeroMQ上下文zmq::context_t context(1); // 創建一個PUSH類型的套接字,用于推送消息zmq::socket_t socket(context, zmq::socket_type::push); // 將套接字綁定到地址"tcp://*:5557"socket.bind("tcp://*:5557"); for (int i = 0; i < 10; ++i) {// 生成任務消息,這里以任務編號作為消息內容std::string task = "Task " + std::to_string(i); zmq::message_t msg(task.size()); memcpy(msg.data(), task.data(), task.size()); socket.send(msg, zmq::send_flags::none); std::cout << "Pushed task: " << task << std::endl;}return 0;
}
// 拉取端代碼
#include <zmq.hpp>
#include <iostream>int main() {// 創建ZeroMQ上下文zmq::context_t context(1); // 創建一個PULL類型的套接字,用于拉取消息zmq::socket_t socket(context, zmq::socket_type::pull); // 連接到推送端的地址"tcp://localhost:5557"socket.connect("tcp://localhost:5557"); while (true) {// 接收任務消息zmq::message_t msg; socket.recv(msg, zmq::recv_flags::none); std::string task(static_cast<char*>(msg.data()), msg.size()); std::cout << "Pulled task: " << task << std::endl;}return 0;
}

在這個示例中,推送端生成 10 個任務消息并推送給拉取端,拉取端不斷接收任務消息并打印。在實際應用中,可能會有多個拉取端同時從推送端拉取消息,實現任務的并行處理和負載均衡 。例如在一個分布式計算集群中,任務調度中心作為推送端將計算任務分發給各個計算節點(拉取端),各個計算節點并行處理任務,提高整體的計算效率。

四、ZeroMQ應用場景

4.1分布式系統

在分布式系統中,各個節點之間需要進行高效的通信和協作。ZeroMQ 可以作為節點間通信的橋梁,實現數據的傳輸和任務的分發。在一個分布式文件系統中,客戶端節點向元數據節點發送文件讀取請求,元數據節點通過 ZeroMQ 將請求轉發給存儲節點,存儲節點讀取文件數據后,再通過 ZeroMQ 將數據返回給客戶端節點。這種方式能夠實現分布式系統中各節點之間的高效通信,提高系統的整體性能。

4.2實時數據處理

在實時數據處理領域,如金融交易系統、物聯網數據采集與分析等場景中,需要處理大規模的數據流和事件驅動的應用。ZeroMQ 的高性能和低延遲特性使其非常適合這類場景。在一個股票交易系統中,行情數據會實時產生并需要快速處理和分發。使用 ZeroMQ 的發布 - 訂閱模式,行情數據發布者可以將實時的股票價格、成交量等數據發布出去,各個交易策略模塊作為訂閱者,能夠及時接收到這些數據并進行分析和交易決策。這種方式確保了數據的快速傳輸和處理,滿足了實時性的要求。

4.3多線程并發編程

在多線程環境下,線程之間的通信和協作是一個重要問題。ZeroMQ 提供了進程內(inproc)通信協議,專門用于同一進程內不同線程之間的通信。以一個多線程的圖像識別程序為例,主線程負責讀取圖像文件,然后通過 ZeroMQ 將圖像數據發送給多個工作線程進行識別處理。工作線程處理完成后,再通過 ZeroMQ 將識別結果返回給主線程。通過這種方式,利用 ZeroMQ 實現了線程間的高效通信和任務協作,避免了傳統多線程編程中復雜的同步和互斥操作,提高了程序的并發性能和可維護性。

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

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

相關文章

深入解析 COUNT(DISTINCT) OVER(ORDER BY):原理、問題與高效替代方案

目錄 一、累計去重需求場景 二、COUNT(DISTINCT) OVER(ORDER BY) 語法解析 2.1 基礎語法 2.2 執行原理 三、三大核心問題分析

線性數據結構:單向鏈表

放棄眼高手低&#xff0c;你真正投入學習&#xff0c;會因為找到一個新方法產生成就感&#xff0c;學習不僅是片面的記單詞、學高數......只要是提升自己的過程&#xff0c;探索到了未知&#xff0c;就是學習。 目錄 一.鏈表的理解 二.鏈表的分類&#xff08;重點理解&#xf…

基于PyQt5打造的實用工具——PDF文件加圖片水印,可調大小位置,可批量處理!

01 項目簡介 &#xff08;1&#xff09;項目背景 隨著PDF文件在信息交流中的廣泛應用&#xff0c;用戶對圖片水印的添加提出了更高要求&#xff0c;既要美觀&#xff0c;又需高效處理批量文件。現有工具難以實現精確調整和快速批量操作&#xff0c;操作繁瑣且效果不理想。本項…

MCU內部ADC模塊誤差如何校準

本文章是筆者整理的備忘筆記。希望在幫助自己溫習避免遺忘的同時&#xff0c;也能幫助其他需要參考的朋友。如有謬誤&#xff0c;歡迎大家進行指正。 一、ADC誤差校準引言 MCU 片內 ADC 模塊的誤差總包括了 5 個靜態參數 (靜態失調&#xff0c;增益誤差&#xff0c;微分非線性…

嵌入式硬件篇---CPUGPUTPU

文章目錄 第一部分&#xff1a;處理器CPU&#xff08;中央處理器&#xff09;1.通用性2.核心數3.緩存4.指令集5.功耗和發熱 GPU&#xff08;圖形處理器&#xff09;1.并行處理2.核心數量3.內存帶寬4.專門的應用 TPU&#xff08;張量處理單元&#xff09;1.為深度學習定制2.低精…

03-機器學習-數據獲取

一、流行機器學習數據集 主流機器學習數據集匯總 數據集名稱描述來源MNIST手寫數字圖像數據集&#xff0c;由美國人口普查局員工書寫。MNIST官網ImageNet包含數百萬張圖像&#xff0c;用于圖像分類和目標檢測。ImageNet官網AudioSet包含YouTube音頻片段&#xff0c;用于聲音分…

doris:STRUCT

STRUCT<field_name:field_type [COMMENT comment_string], ... > 表示由多個 Field 組成的結構體&#xff0c;也可被理解為多個列的集合。 不能作為 Key 使用&#xff0c;目前 STRUCT 僅支持在 Duplicate 模型的表中使用。一個 Struct 中的 Field 的名字和數量固定&…

一次端口監聽正常,tcpdump無法監聽到指定端口報文問題分析

tcpdump命令&#xff1a; sudo tcpdump -i ens2f0 port 6471 -XXnnvvv 下面是各個部分的詳細解釋&#xff1a; 1.tcpdump: 這是用于捕獲和分析網絡數據包的命令行工具。 2.-i ens2f0: 指定監聽的網絡接口。ens2f0 表示本地網卡&#xff09;&#xff0c;即計算機該指定網絡接口捕…

“新月智能武器系統”CIWS,開啟智能武器的新紀元

新月人物傳記&#xff1a;人物傳記之新月篇-CSDN博客 相關文章鏈接&#xff1a;星際戰爭模擬系統&#xff1a;新月的編程之道-CSDN博客 新月智能護甲系統CMIA--未來戰場的守護者-CSDN博客 “新月之智”智能戰術頭盔系統&#xff08;CITHS&#xff09;-CSDN博客 目錄 智能武…

實驗六 項目二 簡易信號發生器的設計與實現 (HEU)

聲明&#xff1a;代碼部分使用了AI工具 實驗六 綜合考核 Quartus 18.0 FPGA 5CSXFC6D6F31C6N 1. 實驗項目 要求利用硬件描述語言Verilog&#xff08;或VHDL&#xff09;、圖形描述方式、IP核&#xff0c;結合數字系統設計方法&#xff0c;在Quartus開發環境下&#xff…

SCRM系統如何提升客戶管理及業務協同的效率與價值

內容概要 在當今商業環境中&#xff0c;SCRM系統&#xff08;社交客戶關系管理系統&#xff09;正逐漸受到越來越多企業的關注和重視。隨著科技的發展&#xff0c;傳統的客戶管理方式已經無法滿足快速變化的市場需求&#xff0c;SCRM系統通過整合客戶數據和社交網絡信息&#…

[免費]微信小程序智能商城系統(uniapp+Springboot后端+vue管理端)【論文+源碼+SQL腳本】

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;看到一個不錯的微信小程序智能商城系統(uniappSpringboot后端vue管理端)&#xff0c;分享下哈。 項目視頻演示 【免費】微信小程序智能商城系統(uniappSpringboot后端vue管理端) Java畢業設計_嗶哩嗶哩_bilibili 項目介紹…

PID算法的數學實現和參數確定方法

目錄 概述 1 算法描述 1.1 PID算法模型 1.2 PID離散化的圖形描述 1.3 PID算法的特點 2 離散化的PID算法 2.1 位置式PID算法 2.2 增量式PID算法 2.3 位置式PID與增量式PID比較 3 控制器參數整定 3.1 PID參數確定方法 3.1.1 湊試法 3.1.2 臨界比例法 3.1.3 經驗法…

《DeepSeek R1:大模型最簡安裝秘籍》

DeepSeek R1&#xff1a;AI 大模型界的新起之秀 在人工智能的璀璨星空中&#xff0c;大模型如繁星般閃耀&#xff0c;而 DeepSeek R1 無疑是其中一顆冉冉升起的新星&#xff0c;自問世以來便吸引了全球的目光&#xff0c;在人工智能領域占據了重要的一席之地。 從性能表現上看…

【論文閱讀】RAG-Reward: Optimizing RAG with Reward Modeling and RLHF

研究背景 研究問題&#xff1a;這篇文章要解決的問題是如何優化檢索增強生成&#xff08;RAG&#xff09;系統&#xff0c;特別是通過獎勵建模和人類反饋強化學習&#xff08;RLHF&#xff09;來提高大型語言模型&#xff08;LLMs&#xff09;在RAG任務中的效果。研究難點&…

【數據結構】(3)包裝類和泛型

一、包裝類 1、什么是包裝類 將基礎類型包裝成的類就是包裝類。由于基礎類型不是繼承 Object 類的類&#xff0c;所以在泛型不能直接支持基礎類型&#xff0c;為了解決這個問題&#xff0c;就需要把基礎類型轉換為對應的包裝類。 基礎類型對應的包裝類 基礎類型包裝類byteByte…

DBUtils中QueryRunner(空參,傳數據源)構造方法的區別及應用場景

關于學習Spring框架時重構DAO層時&#xff0c;遇到的QueryRunner構造方法的問題&#xff0c;回憶MySQL中DBUtils部分 1. 空參構造方法 new QueryRunner() 特點&#xff1a; 不綁定數據源&#xff1a;QueryRunner 實例內部沒有 DataSource&#xff0c;因此無法自動獲取連接。 …

C++11線程

C11提供了線程庫&#xff0c;下面我們來看一下如何使用。 線程的創建 頭文件 要創建一個線程需要包一個線程頭文件:#include <thread> 我們先來看看thread支持的構造方式。 支持默認構造&#xff0c;直接使用thread創建一個空的線程對象。 也支持帶參的構造&#x…

梯度提升用于高效的分類與回歸

人工智能例子匯總:AI常見的算法和例子-CSDN博客 使用 決策樹(Decision Tree) 實現 梯度提升(Gradient Boosting) 主要是模擬 GBDT(Gradient Boosting Decision Trees) 的原理,即: 第一棵樹擬合原始數據計算殘差(負梯度方向)用新的樹去擬合殘差累加所有樹的預測值重…

Golang 并發機制-3:通道(channels)機制詳解

并發編程是一種創建性能優化且響應迅速的軟件的強大方法。Golang&#xff08;也稱為 Go&#xff09;通過通道&#xff08;channels&#xff09;這一特性&#xff0c;能夠可靠且優雅地實現并發通信。本文將揭示通道的概念&#xff0c;解釋其在并發編程中的作用&#xff0c;并提供…