Netty-01-NIO前置知識

目錄

NIO三大組件

一. ByteBuffer

基本用法

DirectByteBuffer與HeapByteBuffer對比

字符串轉ByteBuffer

ByteBuffer.wrap(byte[] )

粘包與拆包

文件編程

零拷貝transferTo

二. 阻塞與非阻塞Channel

三. Selector

SelectionKey(重點)

SelectionKey四種類型

key.cancel()

事件類型

iter.remove()

selectKeys中的事件要么處理、要么取消

key.cancel()的應用場景

selectionKey附件

TCP編程都要考慮的:消息邊界處理

ByteBuffer大小分配

寫出內容過多問題-使用write事件(重點)

通過可寫事件-分批多次進行大文件寫出

多路復用器

selector.select()如何退出阻塞

NIO多線程優化

阻塞隊列還可以放入Runnable任務

NIO概念剖析

BIO與NIO

IO模型

阻塞read()

非阻塞read()

異步IO

內存映射與零拷貝

mmap+write

sendFile

再次對比DirectByteBuffer與HeapByteBuffer

為何IO操作都需要將JVM內存數據拷貝到堆外內存?

那為什么Java不全部通過JNI操作堆外內存來寫代碼呢?

普通BIO

DirectByteBuffer存在的意義(重點)

沒有mmap內存映射產生的DirectByteBuffer對象之前

直接內存的開辟示意圖

Nio如何管理堆外內存的釋放

回收流程

Netty的異步調用與異步IO模型

視頻鏈接


NIO三大組件

NIO三大組件:Channel、Buffer、Selector

Selector適合channel連接數特別多,但是每個channel上來回的流量低的場景

一. ByteBuffer

基本用法

int count = channel.read(buffer),如果返回-1,表示channel中的數據讀取完畢了

  • 在寫入模式下,limit就是能最大的寫入位置
  • 在讀取模式下,limit就是最大的讀取位置

compact()兩個作用:壓縮、切換到寫模式

DirectByteBuffer與HeapByteBuffer對比

HeapByteBuffer因為是分配在JVM堆上的,所以如果一輪gc這個HeapByteBuffer還沒有被處理完,就不能被回收,那么如果此HeapByteBuffer在新生代,就會被通過復制算法,復制到s區(產生多次數據拷貝),這就是HeapByteBuffer的使用,會受到gc影響的一種表現

DirectByteBuffer,是分配在JVM堆外的,是要調用,系統調用,直接在JVM進程堆(JVM堆外)中分配的,所以分配效率較HeapByteBuffer低一些,但是因為JVM進程堆不受gc影響,不會因為發生了GC,而導致DirectByteBuffer對應的堆外空間被到處復制轉移(復制轉移是會產生開銷的)

字符串轉ByteBuffer

第一種模式寫入數據后,要先flip()切換為讀模式,否則就會讀到空數據

ByteBuffer.wrap(byte[] )

wrap效果等于第二種模式,包裝以后,也會直接切換為讀模式

粘包與拆包

發送方,肯定是想一次攢多條消息一起發送,效率更高,所以就是因為發送方想一次攢一波兒數據一起發,從而導致接收方,產生了粘包的可能

比如上面,發送方可能就是把第8、9、10行的三個數據包,一起發送給了接收方

文件編程

channel的寫入能力是有上限的,如果channel對應的本地發送緩沖區滿了,tcp還沒來得及把堆在發送緩沖區中的數據發出去,此時channel.write()就往發送緩沖區寫入數據就會一直寫入0字節,所以要通過上面的hasRemaining()的方式,分多次往channel中寫入

零拷貝transferTo

當要傳輸的文件大于2G時,要分多次傳輸

我覺得上面函數的第二個參數,應該寫size,不應該是left

二. 阻塞與非阻塞Channel

IO的api默認是阻塞模式,如果想使用非阻塞NIO,那么ServerSocketChannel和SocketChannel都要分別設置非阻塞模式

三. Selector

SelectionKey(重點)

無論是serverSocketChannel,還是socketChannel,往selector上注冊后,都會返回唯一一個魏穎的SelectionKey注冊鍵

  • serverSocketChannel對應唯一一個SelectionKey,可以關注accept事件
  • 從每個socketChannel對應SelectionKey身上,可以取出唯一對應的那個socketChannel,和一個attachment附件
  • 一個socketChannel對應的唯一SelectionKey,可以同時關注read事件和write事件
  • 當socketChannel上無論有read事件,還是write事件時,selector.select()都會將這個socketChannel對應的唯一SelectionKey返回。但是可以通過key.readable()或者key.writeable()判斷此時返回的事件究竟是何種類型

SelectionKey四種類型

sscKey關注了accept事件

那么當客戶端有新的連接進來時,sscKey這個SelectionKey就會出現在selector.select()返回的SelectionKey的集合中。也就是說,此時代碼B處的某一個key才可能是sscKey

可以看到,當有一個連接事件進來時,selector.select()返回的SelectionKey還是第28行對應的sscKey,只不過在第28行時,sscKey管理的serverSocketChannel上還沒有accept事件達到。

而當有新的客戶端連接serverSocketChannel時,就代表sscKey管理的serverSocketChannel上有accept事件達到了,此時,selector.select()返回的SelectionKey還是第28行對應的sscKey,此時,通過sscKey就能拿到這個新達到的accept事件

key.cancel()

如果有了事件不處理,上面的代碼就會一直死循環,因為selector.select()一直會把sscKey給返回

如果拿到事件就是不想處理這個事件,可以cancel

此時key.cancel()實現的效果是,直接把此selectionKey從selector身上拿掉了,也就是說,以后本selector就不再管理此selectionKey對應的channel了,也就是說,以后本selector就不再監控來自這個channel上的任何事件了(這個channel就放養了,無人看管,無人關心了)

事件類型

iter.remove()

當第42行,將accept事件處理以后,就會將sscKey@59a6e353這個selectionKey上的accept事件給去掉

sscKey@59a6e353這個selectionKey此時,還在綠色框中,也就是selector.select()時,還是會將這個selectionKey給拿出來,但是這個selectionKey上此時已經沒有accept事件了

第二輪while循環,此時selector.select()返回的集合中有兩個selectionKey,一個sscKey和scKey,只不過sscKey上的accept事件已經不存在了,在上一輪while循環被取走了,所以此時在想通過sscKey上取accept事件就取不到了,所以上面代碼第42行會返回null,導致代碼第43行報了NPE

講了這么多,我們處理完一個selector.select()返回的集合中的某個selectionKey身上掛載的事件后,就要把這個selectionKey,從綠色框的selectionKey集合中給移除

右邊紅色框,就是每個selector的紅黑樹集合,這個保存著所有注冊到本selector身上的channel(包括serverSocketChannel和socketChannel)

右邊綠色框,就是每次epoll_wait()返回的“有事件到達的selectionKey集合”,只不過每次用戶空間把這個集合取過去后,處理完每個selectionKey當前身上掛載的事件后,需要用戶空間手動調用iter.remove()把這個selectionKey,從“有事件到達的selectionKey集合”中手動刪除

selectKeys中的事件要么處理、要么取消

當客戶端連接強制斷開時(比如客戶端直接宕機了),服務端再通過客戶端的channel去read()就會拋異常,會導致服務端線程掛掉,所以服務端需要catch這個IO異常

key.cancel()的應用場景

客戶端連接關閉后,會引發一個Read事件,讀取這個Read事件會拋出IOExcepiton

所以此時要做的就是,通過調用key.cancel(),取消此selectionKey背后對應的channel,在selector身上的注冊,讓selector不再管理這個已經死了的客戶端channel了

不管是客戶端強制斷開,還是正常channel.close(),服務端都會受到一個read事件

只不過異常斷開時,服務端調用channel.read()會拋出IOException。正常斷開時,服務端調用channel.read()會返回-1。

但是不管是客戶端連接時強制斷開,還是正常channel.close()導致的斷開,服務端都要通過key.cancel(),把這個channel對應的SelectionKey從selector的紅黑樹中刪除,也就是,刪除這個channel在selector身上的注冊行為

selectionKey附件

SelectionKey - Channel - attachment,都是一一對應的

通過這種方式,給每個channel附帶一個唯一對應的ByteBuffer

TCP編程都要考慮的:消息邊界處理

通過fileSize(固定字節數) + fileBuffer(可變字節數)的形式,來處理消息邊界問題。

http也是這種形式,有contentLength請求頭

ByteBuffer大小分配

寫出內容過多問題-使用write事件(重點)

可以看到發送能力是有限的,當發送緩沖區寫滿了,第38行的寫入就會寫不進去了,所以返回的寫入字節數是0

這樣的實現模式,本身是沒有問題的,如果3000w字節的數據,如果沒有發送完,那么左側的epoll處理主線程就會一直卡在上圖紅框的while循環中,別的channel的話,epoll處理主線程此時就無暇去處理它們。這不符合我們非阻塞IO的設計思想

我們希望是,當發送緩沖區滿時,epoll處理主線程就不一直卡在上圖紅框的while循環中(產生空轉,因為只要發送緩沖區滿,while循環就會一直空轉耗費CPU),而是去處理別的channel的事件

(等上一個channel的發送緩沖區空了,觸發一個Write事件,表示發送緩沖區空了,可以寫了,此時,epoll處理主線程再對這個channel寫入上一輪沒有寫完的數據)

此時,寫事件會覆蓋關注的讀事件

此時,就表示我們通過scKey這一個SelectionKey,同時關注了Read和Write事件

此后,如果此scKey對應的channel對應的發送緩沖區空了以后,就會觸發一個Write的可寫事件,然后代碼就會進入第47行的if判斷中,開始把上一輪沒有寫完的buffer中的剩余數據,繼續去寫

這樣,就把原來的while循環寫,變成了對Write事件的多次觸發

大數據buffer寫完后,還需要將它從附件處刪除,同時取消關注Write事件

可寫事件Write的觸發機制,就是當發送緩沖區空時,可以寫數據了,那么就會觸發可寫事件,從而epoll.select()就能拿到這個可寫事件

通過可寫事件-分批多次進行大文件寫出

可寫事件,只有在要寫的數據太多時,才去使用

通過可寫事件,來處理channel.write()一次寫不完一整個大文件的情況。

那么就可以知道,如果你每次就寫個幾個字節,幾十上百個字節,那么直接通過channel.write(),把數據一次性寫出去就好了,根本不需要可寫事件Write的相關邏輯參與

可以看到,當這個channel對應的發送緩沖區寫滿的時候,再通過channel.write()來往channel對應的發送緩沖區中寫數據,會直接返回0,也就是寫入了0字節。

后續,寫入線程就會多次空轉,嘗試往發送緩沖區中寫入數據,那么我們可以通過,寫入發送緩沖區寫滿了以后,就通過selectionKey關注這個channel的write事件,那么當這個channel對應的發送緩沖區又可以寫數據時,selector.select()就會探測到一個write可寫事件,這個時候,我們的寫入線程,再通過channel.write()來往channel對應的發送緩沖區中寫數據,就能避免寫入線程因為發送換成區滿而產生的多次空轉問題

那么此時,如果第36行一次沒有把全部大文件寫完,那么就會多次進入第47行開始的可寫事件,也就是相當于,把原來的while循環寫,轉化為了多次可寫事件的處理

因為key身上掛的附件buffer,可能大小有1個G,當我們把這個附件buffer中的數據全部寫完了以后,要取消附件buffer的掛載,讓JVM去gc回收這篇1個G的大內存,不然一直讓它占著1個G的大內存是非常不合適的

另外,我們把1個G的內容寫完了以后,還要取消關注可寫事件,等到后續又有新的大文件的寫需求時,先嘗試寫一次,如果又沒有寫完,再讓selector去關注這個channel上的可寫事件

多路復用器

selector.select()如何退出阻塞

NIO多線程優化

這里實際上,就是boss線程,再往Worker的阻塞隊列中,投入了一個Runnable任務,并喚醒在selector.select()處阻塞worker線程

阻塞隊列還可以放入Runnable任務

當selector.select()處于阻塞狀態時,直接socketChannel.register(selector)來往selector上注冊事件監聽時,也會被阻塞住

只有先讓worker線程,從selector.select()被喚醒,然后worker線程自己從阻塞隊列中去task執行

注意,當前阻塞隊列中,放的是一個Runnable任務

NIO概念剖析

BIO與NIO

BIO是更加高層次的API,比如它們不會關心到發送緩沖區,接收緩沖區的邏輯。比如,前面的例子,寫大量數據時,就會關心發送區慢了,本次就不發了,而是去關注write事件,等下一次write事件達到時,再去發

IO模型

阻塞read()

非阻塞read()

比如有網卡的數據真正達到網卡緩沖區,需要被從網卡緩沖區復制到內核緩沖區時,用戶線程調用的read()一樣還是會被阻塞住

多路復用器

BIO 同步阻塞、NIO 和 EPOLL都是同步非阻塞

異步IO

異步,一定是非阻塞的。沒有異步阻塞的說法

內存映射與零拷貝

mmap+write

使用MappedByteBuffer

內存映射

內存文件映射適用于對大文件的讀寫。虛擬地址空間有一塊區域: “Memory mapped region for shared libraries” ,這段區域就是在內存映射文件的時候將某一段的虛擬地址和文件對象的某一部分建立起映射關系,此時并沒有拷貝數據到內存中去,而是當進程代碼第一次引用這段代碼內的虛擬地址時,觸發了缺頁異常,這時候OS根據映射關系直接將文件的相關部分數據拷貝到進程的用戶私有空間中去

只用3次拷貝,減少了1次

rocketmq是mmap+write,kafka是sendFile

sendFile

數據,不在經過用戶空間,

零拷貝,指的是不再需要把數據,拷貝到用戶空間內存(JVM內存,就是用戶空間內存

不再需要CPU拷貝,只需要DMA去執行拷貝動作

再次對比DirectByteBuffer與HeapByteBuffer

為何IO操作都需要將JVM內存數據拷貝到堆外內存?

  • 因為JNI操作的內存空間數據,不能隨便被GC從而挪動位置,所以Java程序,不能通過JNI直接分配內存空間來進行代碼邏輯的書寫,因為可能上一秒通過JNI記錄的是固定地址,而GC會導致固定地址內部的數據被挪走到別處
  • 但是,Java程序可以通過JNI分配堆外內存,并直接操作堆外內存,堆外內存的地址就是固定的,不會隨意被GC動作給挪走

那為什么Java不全部通過JNI操作堆外內存來寫代碼呢?
  • 因為,Java的特色就是能自動進行垃圾回收,而只有堆內內存空間的數據,才能通過JVM垃圾收集器進行自動的垃圾回收
  • 所以,如果Java全部使用JNI分配堆外內存,那么就沒有辦法再使用JVM提供的垃圾收集器進行自動的垃圾回收
  • 而JNI操作的堆外內存,才是直面各底層硬件的內存,所有的硬件上的數據讀寫都要先經過堆外內存

JVM堆內內存,JNI是不能直接訪問和操作的。所以,JVM堆內空間要想拿到磁盤中的數據,必須先通過把磁盤數據拿到內核緩沖區中來,然后Java代碼再通過read()系統調用,從內核緩沖區中拿數據到JVM堆內來

不能直接通過調用JNI方法,把內核緩沖區中的數據,寫入到JVM堆內。只能是磁盤數據先到內核緩沖區,然后用戶通過read()系統調用,把內核緩沖區中的數據,再讀取JVM堆內

DirectByteBuffer分配的堆外內存,本質是調用操作系統的malloc(),分配的JVM進程的用戶堆空間的內存

因為,DirectByteBuffer對應的堆外地址,和內核緩沖區,共用同一個片物理頁。所以,我們程序員通過Java代碼往DirectByteBuffer對應的堆外地址put了一些數據,就相當于直接寫入了內核緩沖區,而且put操作,還不會產生系統調用

普通BIO

但是,JDK還是提供了一種走捷徑的方式,通過內存映射mmap,拿到文件映射的address,從而用戶可以通過Java代碼,直接讀寫這個address對應的堆外內存空間

而不用先在JVM堆內用戶空間搞一個byte[],然后再把這個byte[]中的內容寫入堆外內存空間,或者把堆外內存空間的數據讀入byte[]中,從而減少了數據在JVM堆內的拷貝過程

DirectByteBuffer存在的意義(重點)

  • 有了mmap內存映射產生的DirectByteBuffer對象,使得Java程序,也有了能直接操作堆外內存地址空間的機會
  • 堆外內存空間才是直接面對各個底層硬件的,比如底層的磁盤,或者網卡
  • 只有堆外內存空間的數據,才是能直接去往各底層硬件讀寫的,JVM堆內空間數據,是不能直接往各底層硬件讀寫的

沒有mmap內存映射產生的DirectByteBuffer對象之前

Java程序,想將數據寫入到網卡,只能先在Java用戶空間生成byte[]并寫滿數據,然后通過調用write()系統調用先把用戶空間的byte[]寫入到堆外內存空間,然后通過系統調用flush(),把堆外內存空間的數據,寫入到網卡緩沖區

直接內存的開辟示意圖

比如定義:DirectByteBuffer dbb =?ByteBuffer.allocateDirect(1024);底層是:

public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);
}

示意圖:

Nio如何管理堆外內存的釋放

回收流程

NIO中如何使用虛引用管理堆外內存原理_虛引用 堆外內存-CSDN博客

Netty的異步調用與異步IO模型

Netty的異步指的是調用方式的異步,不是指的IO模型的異步。指的是請求的發送,和響應的接收,分別是不同的IO線程在處理

Netty的IO模型還是基于多路復用器的同步非阻塞IO


?

視頻鏈接

黑馬程序員Netty全套教程, netty深入淺出Java網絡編程教程_嗶哩嗶哩_bilibili

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

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

相關文章

知識點17:多Agent系統架構設計模式

知識點17:多Agent系統架構設計模式 核心概念 掌握系統架構思維,理解多Agent系統的設計原則和模式 架構設計核心概念 在構建多Agent系統時,良好的架構設計是系統成功的關鍵。本節將介紹多Agent系統架構設計中的核心概念,包括單點瓶…

數據庫造神計劃第五天---增刪改查(CRUD)(1)

🔥個人主頁:尋星探路 🎬作者簡介:Java研發方向學習者 📖個人專欄:《從青銅到王者,就差這講數據結構!!!》、 《JAVA(SE)----如此簡單&a…

基于Vue3的人工智能生成內容標識服務平臺前端頁面設計

效果圖&#xff1a;素材庫&#xff1a;App.vue<template><div id"app"><!-- 頭部導航 --><Header /><!-- 主要內容區域 --><main class"main-content"><div class"container"><!-- 強制性國家標準…

使用 MyCat 實現 MySQL 主從讀寫分離

文章目錄使用 MyCat 實現 MySQL 主從讀寫分離完整指南一、MySQL 讀寫分離基礎概述1.1 讀寫分離工作原理1.2 為什么需要讀寫分離1.3 讀寫分離的兩種實現方式主流讀寫分離中間件對比二、MyCat 中間件簡介2.1 MyCat 核心功能2.2 MyCat 適用場景三、環境準備與 MyCat 安裝3.1 前提&…

物聯網傳感器檢測實驗

/*------------------------------------------------------------------------------ * @文件名 : handle * @描述 : 用戶處理函數 * @作者 : 物聯網項目組 * @日期 : 2023/04/01 * @版本 : V0.0.2 *****************************…

什么是dirsearch、xray、durpsuite、sqlmap?

你提到的 dirsearch、xray、durpsuite&#xff08;可能為筆誤&#xff0c;推測是 ??Burp Suite??&#xff09;和 sqlmap 均為網絡安全領域中常用的工具&#xff0c;主要用于 Web 應用的安全測試、漏洞檢測或滲透測試。以下分別詳細說明&#xff1a;??1. dirsearch????…

lamp腳本部署

#!/bin/bash #關閉防火墻和selinux systemctl stop firewalld systemctl disable firewalld setenforce 0 #配置yum網絡源 echo “正在配置yum倉庫” rm -rf /etc/yum.repos.d/* wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo &am…

Redis Hash數據類型深度解析:從命令、原理到實戰場景

前言 在Redis的眾多數據結構中&#xff0c;Hash&#xff08;哈希&#xff09;類型占據著至關重要的地位。Redis本身就是一個高性能的鍵值&#xff08;Key-Value&#xff09;數據庫&#xff0c;其底層的鍵值對便是通過哈希方式組織的。而Hash數據類型則更進一步&#xff0c;它允…

【C++實戰?】解鎖C++文件操作:從基礎到實戰的進階之路

目錄一、文件操作的基本概念1.1 文件的分類與打開方式1.2 文件流的概念與相關類&#xff08;ifstream、ofstream、fstream&#xff09;1.3 文件操作的基本流程二、文本文件的讀寫實戰2.1 文本文件的打開與關閉2.2 文本文件的寫入操作&#xff08;<< 運算符、write 函數&a…

從C++開始的編程生活(9)——模板初階

前言 本系列文章承接C語言的學習&#xff0c;需要有C語言的基礎才能學會哦~ 第8篇主要講的是有關于C的模板初階。 C才起步&#xff0c;都很簡單&#xff01;&#xff01; 目錄 前言 模板初階 基本語法 函數模板的實例化 顯式實例化的作用 類模板 基本語法 模板初階 模板…

計算機網絡——傳輸層(25王道最新版)

傳輸層傳輸層提供的服務進程 端口號 傳輸層協議之間的關系socket套接字有鏈接 VS 無連接 | 可靠 VS 不可靠UDP數據報及檢驗數據報格式檢驗方法TCPTCP協議的三大階段TCP報文段格式&#xff08;很重要&#xff09;建立連接&#xff08;三次握手&#xff09;&#xff08;超級超級重…

羽毛球地板:從專業運動場景到全民健身市場的技術躍遷與產業重構

在全球體育產業向“專業化大眾化”雙軌并行的趨勢下&#xff0c;羽毛球地板作為運動場景的核心基礎設施&#xff0c;正經歷從單一功能型產品向“性能優化場景適配智能管理”一體化解決方案的轉型。據QYResearch統計&#xff0c;2031年全球羽毛球地板市場規模將達15.95億元&…

R 語言查看類庫源碼的方法

你想查看 getGEO&#xff08;來自 R 語言 GEOquery 包&#xff09;的源碼&#xff0c;這能幫你更好理解其工作原理和數據處理細節。由于 getGEO 是 R 函數&#xff0c;查看方法與 Python 有所不同。下面為你提供幾種主要方法。 方法 適用場景 關鍵命令/操作 在 R 控制臺直接查看…

SQL,posexplode 用法示例

示例1 -- 創建測試數據 WITH test_data AS (SELECT array(apple, banana, cherry) as fruits ) SELECT pos, col FROM test_data LATERAL VIEW posexplode(fruits) t AS pos, col;結果 pos | col ----|------- 0 | apple 1 | banana 2 | cherry示例2 -- 假設有一個用戶表…

數據庫造神計劃第十天---數據庫約束(1)

&#x1f525;個人主頁&#xff1a;尋星探路 &#x1f3ac;作者簡介&#xff1a;Java研發方向學習者 &#x1f4d6;個人專欄&#xff1a;《從青銅到王者&#xff0c;就差這講數據結構&#xff01;&#xff01;&#xff01;》、 《JAVA&#xff08;SE&#xff09;----如此簡單&a…

知微傳感Dkam系列3D相機SDK例程篇:CSharp連接相機及保存數據

序言 寫在前面 本人從事機器視覺細分的3D相機行業。編寫此系列文章主要目的有&#xff1a; 1、便利他人應用相機&#xff0c;本系列文章包含公司所出售相機的SDK的使用例程及詳細注釋&#xff1b;2、促進行業發展及交流。 知微傳感Dkam系列3D相機可以應用于定位分揀、焊接焊縫提…

[筆記] 系統分析師 第十二章 軟件架構設計(分析師主要工作)

文章目錄前言12.1 軟件架構概述12.1.1 軟件架構的意義12.1.2 軟件架構的發展史12.2 軟件架構建模12.3 軟件架構風格12.3.1 軟件架構風格概述12.3.2 數據流體系結構風格1.批處理體系結構風格2.管道-過濾體系結構風格12.3.3 調用/返回體系結構風格1.主程序/子程序風格2.面向對象體…

C++---存儲周期,作用域,鏈接性

在C程序設計中&#xff0c;變量的行為不僅由其類型決定&#xff0c;還由存儲周期&#xff08;變量在內存中存在的時間&#xff09;、作用域&#xff08;變量可被訪問的代碼范圍&#xff09;和鏈接性&#xff08;變量在多文件程序中的可見性&#xff09;共同約束。 一、存儲周期…

基于Python的商品爬取與可視化系統

本系統是基于Python的商品數據爬取與價格分析可視化系統&#xff0c;集成了數據爬取、數據存儲、數據展示和可視化分析等功能。下面介紹一下系統主要功能和技術棧。一、主要功能&#xff1a;1、數據爬取功能 支持淘寶美妝商品數據爬取 可配置搜索關鍵詞和爬取頁數 實時顯示爬取…

聯邦學習過程中,了解清楚影響準確率的因素有哪些也很重要

影響模型準確率的因素有很多&#xff0c;下面是一些主要的因素&#xff0c;它們可以從數據、模型設計、訓練策略以及超參數等多個層面來考慮。1. 學習率作用&#xff1a;學習率直接影響模型的訓練速度、穩定性和最終表現。過高的學習率可能導致模型不收斂或收斂不穩定&#xff…