Netty源碼—2.Reactor線程模型二

大綱

1.關于NioEventLoop的問題整理

2.理解Reactor線程模型主要分三部分

3.NioEventLoop的創建

4.NioEventLoop的啟動

4.NioEventLoop的啟動

(1)啟動NioEventLoop的兩大入口

(2)判斷當前線程是否是NioEventLoop線程

(3)創建一個線程并啟動

(4)NioEventLoop的啟動總結

(1)啟動NioEventLoop的兩大入口

入口一:服務端啟動,注冊服務端Channel到Selector時

入口二:新連接接入,通過chooser綁定一個NioEventLoop時

下面先看入口一:

調用ServerBootstrap的bind()方法其實會調用AbstractBootstrap的doBind()方法,然后會調用AbstractBootstrap的initAndRegister()方法,接著執行config().group().register(channel)注冊服務端Channel。最后會逐層深入調用到AbstractChannel.AbstractUnsafe的register()方法,來啟動一個NioEventLoop將服務端Channel注冊到Selector上。

//Bootstrap sub-class which allows easy bootstrap of ServerChannel
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {......
}//AbstractBootstrap is a helper class that makes it easy to bootstrap a Channel. 
//It support method-chaining to provide an easy way to configure the AbstractBootstrap.
//When not used in a ServerBootstrap context, the #bind() methods are useful for connectionless transports such as datagram (UDP).
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {...//Create a new Channel and bind it.public ChannelFuture bind(int inetPort) {//首先根據端口號創建一個InetSocketAddress對象,然后調用重載方法bind()return bind(new InetSocketAddress(inetPort));}//Create a new Channel and bind it.public ChannelFuture bind(SocketAddress localAddress) {//驗證服務啟動需要的必要參數validate();if (localAddress == null) throw new NullPointerException("localAddress");return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));}private ChannelFuture doBind(final SocketAddress localAddress) {final ChannelFuture regFuture = initAndRegister();//1.初始化和注冊Channelfinal Channel channel = regFuture.channel();...doBind0(regFuture, channel, localAddress, promise);//2.綁定服務端端口...return promise;}final ChannelFuture initAndRegister() {Channel channel = null;...//1.創建服務端Channelchannel = channelFactory.newChannel();//2.初始化服務端Channelinit(channel);...//3.注冊服務端Channel,比如通過NioEventLoopGroup的register()方法進行注冊ChannelFuture regFuture = config().group().register(channel);...return regFuture;}...
}//Bootstrap sub-class which allows easy bootstrap of ServerChannel
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);...@Overridepublic final ServerBootstrapConfig config() {return config;}...
}public abstract class AbstractBootstrapConfig<B extends AbstractBootstrap<B, C>, C extends Channel> {protected final B bootstrap;...protected AbstractBootstrapConfig(B bootstrap) {this.bootstrap = ObjectUtil.checkNotNull(bootstrap, "bootstrap");}//Returns the configured EventLoopGroup or null if non is configured yet.public final EventLoopGroup group() {//比如返回一個NioEventLoopGroup對象return bootstrap.group();}...
}//MultithreadEventLoopGroup implementations which is used for NIO Selector based Channels.
public class NioEventLoopGroup extends MultithreadEventLoopGroup {......
}//Abstract base class for EventLoopGroup implementations that handles their tasks with multiple threads at the same time.
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {...@Overridepublic ChannelFuture register(Channel channel) {//先通過next()方法獲取一個NioEventLoop,然后通過NioEventLoop.register()方法注冊服務端Channelreturn next().register(channel);}@Overridepublic EventLoop next() {return (EventLoop) super.next();}...
}//Abstract base class for EventExecutorGroup implementations that handles their tasks with multiple threads at the same time.
public abstract class MultithreadEventExecutorGroup extends AbstractEventExecutorGroup {private final EventExecutorChooserFactory.EventExecutorChooser chooser;...@Overridepublic EventExecutor next() {//通過線程選擇器chooser選擇一個NioEventLoopreturn chooser.next();}    ...
}//SingleThreadEventLoop implementation which register the Channel's to a Selector and so does the multi-plexing of these in the event loop.
public final class NioEventLoop extends SingleThreadEventLoop {......
}//Abstract base class for EventLoops that execute all its submitted tasks in a single thread.
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {...@Overridepublic ChannelFuture register(Channel channel) {return register(new DefaultChannelPromise(channel, this));}@Overridepublic ChannelFuture register(final ChannelPromise promise) {ObjectUtil.checkNotNull(promise, "promise");//調用AbstractUnsafe的register()方法promise.channel().unsafe().register(this, promise);return promise;}...
}//A skeletal Channel implementation.
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {private volatile EventLoop eventLoop;...//Unsafe implementation which sub-classes must extend and use.protected abstract class AbstractUnsafe implements Unsafe {...@Overridepublic final void register(EventLoop eventLoop, final ChannelPromise promise) {...//綁定事件循環器,即綁定一個NioEventLoop到該Channel上AbstractChannel.this.eventLoop = eventLoop;//注冊Selector,并啟動一個NioEventLoopif (eventLoop.inEventLoop()) {register0(promise);} else {...//通過啟動這個NioEventLoop線程來調用register0()方法將這個服務端Channel注冊到Selector上//其實執行的是SingleThreadEventExecutor的execute()方法eventLoop.execute(new Runnable() {@Overridepublic void run() {register0(promise);}});...}}}...
}

(2)判斷當前線程是否是NioEventLoop線程

調用NioEventLoop的inEventLoop()方法可以判斷當前線程是否是Netty的Reactor線程,也就是NioEventLoop對應的線程實體。NioEventLoop的線程實體被創建之后,會將該線程實體保存到NioEventLoop父類的成員變量thread中。

服務端啟動、注冊服務端Channel到Selector,執行到AbstractUnsafe.register()方法中的eventLoop.inEventLoop()代碼時,會將main方法對應的主線程傳遞進來與this.thread進行比較。由于this.thread此時并未賦值,所以為空,因此inEventLoop()方法返回false,于是便會執行eventLoop.execute()代碼創建一個線程并啟動。

//SingleThreadEventLoop implementation which register the Channel's 
//to a Selector and so does the multi-plexing of these in the event loop.
public final class NioEventLoop extends SingleThreadEventLoop {......
}//Abstract base class for EventLoops that execute all its submitted tasks in a single thread.
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {......
}//Abstract base class for OrderedEventExecutor's that execute all its submitted tasks in a single thread.
public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {......
}//Abstract base class for EventExecutors that want to support scheduling.
public abstract class AbstractScheduledEventExecutor extends AbstractEventExecutor {......
}//Abstract base class for {@link EventExecutor} implementations.
public abstract class AbstractEventExecutor extends AbstractExecutorService implements EventExecutor {...@Overridepublic boolean inEventLoop() {//注冊服務端Channel時是通過主線程進行注冊的,Thread.currentThread()對應的就是main線程//調用SingleThreadEventExecutor.inEventLoop()方法return inEventLoop(Thread.currentThread());}...
}//Abstract base class for OrderedEventExecutor's that execute all its submitted tasks in a single thread.
public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {private volatile Thread thread;...@Overridepublic boolean inEventLoop(Thread thread) {return thread == this.thread;//此時線程還沒創建,this.thread為null}...
}

(3)創建一個線程并啟動

AbstractUnsafe.register()方法準備將服務端Channel注冊到Selector上時,首先在判斷條件中執行eventLoop.inEventLoop()代碼發現為false,于是便執行eventLoop.execute()代碼創建一個線程并啟動它去執行注冊任務。而執行eventLoop.execute()代碼其實就是調用SingleThreadEventExecutor的execute()方法。

//Abstract base class for EventLoops that execute all its submitted tasks in a single thread.
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {...@Overridepublic ChannelFuture register(Channel channel) {return register(new DefaultChannelPromise(channel, this));}@Overridepublic ChannelFuture register(final ChannelPromise promise) {ObjectUtil.checkNotNull(promise, "promise");//調用AbstractUnsafe的register()方法,并把NioEventLoop自己當作參數傳入promise.channel().unsafe().register(this, promise);return promise;}...
}//A skeletal Channel implementation.
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {private volatile EventLoop eventLoop;...//Unsafe implementation which sub-classes must extend and use.protected abstract class AbstractUnsafe implements Unsafe {...@Overridepublic final void register(EventLoop eventLoop, final ChannelPromise promise) {...//綁定事件循環器,即綁定一個NioEventLoop到該Channel上AbstractChannel.this.eventLoop = eventLoop;//注冊Selector,并啟動一個NioEventLoopif (eventLoop.inEventLoop()) {register0(promise);} else {...//通過啟動這個NioEventLoop線程來調用register0()方法將這個服務端Channel注冊到Selector上//其實執行的是SingleThreadEventExecutor的execute()方法eventLoop.execute(new Runnable() {@Overridepublic void run() {register0(promise);}});...}}}...
}//Abstract base class for OrderedEventExecutor's that execute all its submitted tasks in a single thread.
public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {private volatile Thread thread;//創建NioEventLoop時會通過構造方法傳入NioEventLoopGroup的線程執行器executorprivate final Executor executor;...@Overridepublic void execute(Runnable task) {if (task == null) throw new NullPointerException("task");boolean inEventLoop = inEventLoop();//判斷當前線程是否是Netty的Reactor線程if (inEventLoop) {addTask(task);} else {startThread();addTask(task);if (isShutdown() && removeTask(task)) reject();}if (!addTaskWakesUp && wakesUpForTask(task)) wakeup(inEventLoop);}private void startThread() {//判斷Reactor線程有沒有被啟動;如果沒有被啟動,則通過CAS調用doStartThread()方法啟動線程if (STATE_UPDATER.get(this) == ST_NOT_STARTED) {if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {doStartThread();}}}private void doStartThread() {assert thread == null;//executor.execute()方法會創建出一個FastThreadLocalThread線程來執行Runnable任務//所以在Runnable的run()方法中,Thread.currentThread()指的是這個FastThreadLocalThread線程executor.execute(new Runnable() {@Overridepublic void run() {//Thread.currentThread()指的是FastThreadLocalThread線程thread = Thread.currentThread();...SingleThreadEventExecutor.this.run();//啟動線程...}});}//具體的run()方法由子類比如NioEventLoop來實現protected abstract void run();...
}

SingleThreadEventExecutor的execute()方法的說明如下:

一.這個方法也可能會被用戶代碼使用,如ctx.executor().execute(task)。所以execute()方法里又調用inEventLoop()方法進行了一次外部線程判斷,確保執行task任務時不會遇到線程問題。

二.如果當前線程不是Netty的Reactor線程,則調用startThread()方法啟動一個Reactor線程。在startThread()方法中首先會判斷當前NioEventLoop對應的Reactor線程實體有沒有被啟動。如果沒有被啟動,則通過設置CAS成功后調用doStartThread()方法啟動線程。

三.執行doStartThread()方法時,會調用NioEventLoop的內部成員變量executor的execute()方法。executor就是線程執行器ThreadPerTaskExecutor,它的作用是每次執行Runnable任務時都會創建一個線程來執行。也就是executor.execute()方法會通過DefaultThreadFactory的newThread()方法,創建出一個FastThreadLocalThread線程來執行Runnable任務。

四.doStartThread()方法的Runnable任務會由一個FastThreadLocalThread線程來執行。在Runnable任務的run()方法里,會保存ThreadPerTaskExecutor創建出來的FastThreadLocalThread對象到SingleThreadEventExecutor的成員變量thread中,然后調用SingleThreadEventExecutor的run()方法。

//Abstract base class for EventExecutorGroup implementations that handles their tasks with multiple threads at the same time.
public abstract class MultithreadEventExecutorGroup extends AbstractEventExecutorGroup {private final EventExecutor[] children;private final EventExecutorChooserFactory.EventExecutorChooser chooser;...    //Create a new instance.protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {if (nThreads <= 0) throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));//1.創建ThreadPerTaskExecutor線程執行器if (executor == null) executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());//2.創建NioEventLoopchildren = new EventExecutor[nThreads];for (int i = 0; i < nThreads; i ++) {...//創建每一個NioEventLoop時,都會調用newChild()方法給每一個NioEventLoop配置一些核心參數//傳入線程執行器executor去創建NioEventLoopchildren[i] = newChild(executor, args);}//3.創建線程選擇器chooser = chooserFactory.newChooser(children);...}protected ThreadFactory newDefaultThreadFactory() {//getClass()是獲取該方法所屬的對象類型,也就是NioEventLoopGroup類型//因為是通過NioEventLoopGroup的構造方法層層調用到這里的return new DefaultThreadFactory(getClass());}...
}public final class ThreadPerTaskExecutor implements Executor {private final ThreadFactory threadFactory;public ThreadPerTaskExecutor(ThreadFactory threadFactory) {if (threadFactory == null) throw new NullPointerException("threadFactory");this.threadFactory = threadFactory;}@Overridepublic void execute(Runnable command) {//調用DefaultThreadFactory的newThread()方法執行Runnable任務threadFactory.newThread(command).start();}
}//A ThreadFactory implementation with a simple naming rule.
public class DefaultThreadFactory implements ThreadFactory {private static final AtomicInteger poolId = new AtomicInteger();private final AtomicInteger nextId = new AtomicInteger();private final boolean daemon;private final int priority;protected final ThreadGroup threadGroup;...public DefaultThreadFactory(Class<?> poolType) {this(poolType, false, Thread.NORM_PRIORITY);}public DefaultThreadFactory(Class<?> poolType, boolean daemon, int priority) {//toPoolName()方法會把NioEventLoopGroup的首字母變成小寫this(toPoolName(poolType), daemon, priority);}public DefaultThreadFactory(String poolName, boolean daemon, int priority) {this(poolName, daemon, priority, System.getSecurityManager() == null ? Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup());}public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {...//prefix用來標記線程名字的前綴prefix = poolName + '-' + poolId.incrementAndGet() + '-';this.daemon = daemon;this.priority = priority;this.threadGroup = threadGroup;}@Overridepublic Thread newThread(Runnable r) {Thread t = newThread(new DefaultRunnableDecorator(r), prefix + nextId.incrementAndGet());if (t.isDaemon()) {if (!daemon) t.setDaemon(false);} else {if (daemon) t.setDaemon(true);}if (t.getPriority() != priority) t.setPriority(priority);return t;}protected Thread newThread(Runnable r, String name) {return new FastThreadLocalThread(threadGroup, r, name);}...
}

NioEventLoop是如何與一個線程實體綁定的?NioEventLoop會通過線程執行器ThreadPerTaskExecutor創建一個FastThreadLocalThread,然后再將該FastThreadLocalThread線程保存到其成員變量中,從而實現與一個線程實體進行綁定。

(4)NioEventLoop的啟動總結

一.在注冊服務端Channel的過程中,主線程最終會調用AbstractUnsafe的register()方法。該方法首先會將一個NioEventLoop綁定到這個服務端Channel上,然后把實際注冊Selector的邏輯封裝成一個Runnable任務,接著調用NioEventLoop的execute()方法來執行這個Runnable任務。

二.NioEventLoop的execute()方法其實就是其父類SingleThreadEventExecutor的execute()方法,它會先判斷當前調用execute()方法的線程是不是Netty的Reactor線程,如果不是就調用startThread()方法來創建一個Reactor線程。

三.startThread()方法會通過線程執行器ThreadPerTaskExecutor的execute()方法來創建一個線程。這個線程是一個FastThreadLocalThread線程,這個線程需要執行如下邏輯:把線程保存到NioEventLoop的成員變量thread中,然后調用NioEventLoop的run()方法來啟動NioEventLoop。

NioEventLoop的啟動流程如下:

bind() -> initAndRegister() -> config().group().register() -> eventloop.execute() //入口startThread() -> doStartThread() //創建線程ThreadPerTaskExecutor.execute() //線程執行器創建FastThreadLocalThread線程thread = Thread.currentThread() //保存FastThreadLocalThread線程到NioEventLoop的成員變量中NioEventLoop.run() //啟動NioEventLoop

NioEventLoop的啟動流程說明如下:

首先bind()方法會將具體綁定端口的操作封裝成一個Runnable任務,然后調用NioEventLoop的execute()方法,接著Netty會判斷調用execute()方法的線程是否是NIO線程,如果發現不是就會調用startThread()方法開始創建線程。

創建線程是通過線程執行器ThreadPerTaskExecutor來創建的。線程執行器的作用是每執行一個任務都會創建一個線程,而且創建出來的線程就是NioEventLoop底層的一個FastThreadLocalThread線程。

創建完FastThreadLocalThread線程后會執行一個Runnable任務,該Runnable任務首先會將這個線程保存到NioEventLoop對象。保存的目的是為了判斷后續對NioEventLoop的相關執行線程是否為本身。如果不是就將封裝好的一個任務放入TaskQueue中進行串行執行,實現線程安全。該Runnable任務然后會調用NioEventLoop的run()方法,從而啟動NioEventLoop。NioEventLoop的run()方法是驅動Netty運轉的核心方法。

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

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

相關文章

前端項目中應該如何選擇正確的圖片格式

在前端項目中選擇正確的圖片格式是優化頁面性能、提升用戶體驗的關鍵步驟之一。以下是常見圖片格式的特點、適用場景及選擇建議&#xff0c;幫助你在不同場景下做出最優決策&#xff1a; 一、常見圖片格式對比 格式特點適用場景不適用場景JPEG- 有損壓縮&#xff0c;文件小- 不…

保姆級 STM32 HAL 庫外部中斷教學

1. 外部中斷概述 為什么用外部中斷&#xff1f; 當按鍵按下時&#xff0c;CPU 無需輪詢檢測引腳狀態&#xff0c;而是通過中斷機制立即響應&#xff0c;提高效率&#xff0c;適用于實時性要求高的場景。 關鍵概念 EXTI (External Interrupt/Event Controller)&#xff1a;ST…

Postman高級功能深度解析:Mock Server與自動化監控——構建高效API測試與監控體系

引言&#xff1a;Postman在API開發中的核心價值 在數字化時代&#xff0c;API&#xff08;應用程序編程接口&#xff09;已成為系統間交互的“神經網絡”&#xff0c;其質量直接影響用戶體驗與業務連續性。然而&#xff0c;傳統API測試面臨兩大挑戰&#xff1a; 開發階段依賴…

【程序人生】成功人生架構圖(分層模型)

文章目錄 ?前言?一、根基層——價值觀與使命?二、支柱層——健康與能量?三、驅動層——學習與進化?四、網絡層——關系系統?五、目標層——成就與財富?六、頂層——意義與傳承?外層&#xff1a;調節環——平衡與抗風險?思維導圖 標題詳情作者JosieBook頭銜CSDN博客專家…

【最后203篇系列】020 rocksdb agent

今天還是挺開心的一天&#xff0c;又在工具箱里加了一個工具。嗯&#xff0c;但是快下班的時候也碰到一些不太順心的事&#xff0c;讓我有點惱火。我還真沒想到一個專職的前端&#xff0c;加測試&#xff0c;以及其他一堆人&#xff0c;竟然不知道后端返回的markdown,在前端渲染…

10-- 網絡攻擊防御原理全景解析 | 從單包攻防到DDoS軍團作戰(包你看一遍全記住)

&#x1f6e1;? 網絡攻擊防御原理全景解析 | 從單包攻防到DDoS軍團作戰 如果你也對網絡工程師的內容感興趣的話&#xff0c;歡迎看我的最新文章9–BGP路由黑洞&#xff08;超萬字大解析&#xff09;&#xff1a;網絡世界的“百慕大三角“逃生指南(BGP路由配置實驗含路由黑洞,…

解鎖Python print()函數高級用法

print() 是 Python 中最常用的函數之一,用于將內容輸出到控制臺。雖然它的基本用法非常簡單,但 print() 函數還支持許多高級功能,如格式化輸出、重定向輸出、控制分隔符和結束符等。 1. print() 函數的基本用法 1.1 語法 print() 函數的基本語法如下: print(*objects, …

鬼泣:動作系統3

文章目錄 self-Tag&#xff1a;可以直接在游戲運行時通過標簽區分不同Actorsolid隔離&#xff1a;模塊化低耦合&#xff1a;將功能拆分成多個模塊&#xff0c;修改單一模塊時無需修改其他模塊 動作優先級&#xff1a;當前動作能否打斷上一動作函數不能使用timelineset timer by…

Polymer入門指南:從零開始構建、組織、管理Web Component

前言 Web Component是一種強大的技術&#xff0c;它允許開發者創建可重用的自定義元素&#xff0c;其功能和樣式都與原生HTML元素類似。Polymer是一個用于創建Web Component的庫&#xff0c;簡化了開發過程。今天我們將一起來了解如何基于Polymer開發Web Component。 什么是P…

廣度優先搜索(BFS) vs 深度優先搜索(DFS):算法對比與 C++ 實現

目錄 一、BFS 和 DFS 的核心思想 1. BFS&#xff08;廣度優先搜索&#xff09; 2. DFS&#xff08;深度優先搜索&#xff09; 二、BFS 和 DFS 的對比 三、C 代碼實現 1. BFS 實現&#xff08;鄰接表表示的無向圖&#xff09; 2. DFS 實現&#xff08;遞歸與迭代兩種方式&…

vulhub靶機----基于docker的初探索,環境搭建

環境搭建 首先就是搭建docker環境&#xff0c;這里暫且寫一下 #在kali apt update apt install docker.io配置docker源&#xff0c;位置在/etc/docker/daemon.json {"registry-mirrors": ["https://5tqw56kt.mirror.aliyuncs.com","https://docker…

第7章 類與面向對象

6-1 二維平面上的點操作&#xff08;Python3&#xff09; 題目描述 設計一個表示二維平面上點的類 Point。該類應該包含以下功能&#xff1a; 兩個私有屬性 _x 和 _y&#xff0c;分別表示點的橫坐標和縱坐標。 一個構造函數 __init__&#xff0c;用于初始化點的坐標。 一個…

算法訓練篇06--力扣611.有效三角形的個數

目錄 1.題目鏈接&#xff1a;611.有效三角形的個數 2.題目描述&#xff1a; 3.解法一&#xff1a;(暴力解法)(會超時)&#xff1a; 4.解法二(排序雙指針) 1.題目鏈接&#xff1a;611.有效三角形的個數 2.題目描述&#xff1a; 給定一個包含非負整數的數組 nums &#xf…

網絡編程之解除udp判斷客戶端是否斷開

思路&#xff1a;每幾秒發送一條不顯示的信息&#xff0c;客戶端斷開則不再發送信息&#xff0c;超時則表示客戶端斷開連接。&#xff08;心跳包&#xff09; 服務器 #include <head.h>#define MAX_CLIENTS 100 // 最大支持100個客戶端 #define TIMEOUT 5 // 5秒…

Python Cookbook-4.8 二維陣列變換

任務 需要變換一個列表的列表&#xff0c;將行換成列&#xff0c;列換成行。 解決方案 需要一個列表&#xff0c;其中的每一項都是同樣長度的列表&#xff0c;像這樣 arr [[1,2,3],[4,5,6],[7,8,9],[10,11,12]]列表推導提供了簡單方便的方法以完成二維陣列的轉換: print …

B樹與B+樹在MySQL中的應用:索引

數據結構演示網站&#xff1a;Data Structure Visualization 先來了解兩個數據結構B樹與B樹 B樹&#xff1a; N階B樹每個節點最多存儲N-1個Key&#xff0c;N個指針 例如&#xff1a;一個5階B樹&#xff0c;當前節點存儲到5個Key時&#xff0c;中間的數會向上分離&#xff0c;…

【重構小程序】基于Tika和Langchain4J進行文件解析和文本切片(二)

為了將大語言模型植入到小程序中&#xff0c;來支持用戶的問答。那我們首先需要做的是什么呢&#xff0c;不是引入大語言模型&#xff0c;而且為大語言模型搭建一個私有化知識庫&#xff0c;但是這是這節呢&#xff0c;我們先不搭建私有化知識庫&#xff0c;在這之前&#xff0…

python|exm6-1try-except結構|raise關鍵字|異常類型

目錄 一、try-expect 1. 多個try-expect結構的使用 1.1 捕捉特定異常 1.2 捕捉全部異常 1.3 所有異常合并處理 2. try-except-else-finally 結構 二、raise 關鍵字 一、try-expect try-expect 結構是 Python 中用于異常處理的關鍵機制。它允許你捕獲并處理代碼中可能發生…

小藍的括號串1(棧,藍橋云課)

問題描述 小藍有一個長度為 nn 的括號串&#xff0c;括號串僅由字符 ( 、 ) 構成&#xff0c;請你幫他判斷一下該括號串是否合法&#xff0c;合法請輸出 Yes &#xff0c;反之輸出 No 。 合法括號序列&#xff1a; 空串是合法括號序列。 若 ss 是合法括號序列&#xff0c;則 (…

Centos7配置本地yum源

Centos7配置本地yum源 1、基于iso鏡像的centos源 1.1 準備iso <span style"color:#000000"><span style"background-color:#ffffff"><code class"language-bash"><span style"color:#008000"># 首先看自己使用…