React 第三十六章 Scheduler 任務調度

Scheduler 用于在 React 應用中進行任務調度。它可以幫助開發人員在處理復雜的任務和操作時更好地管理和優化性能。

關于 Scheduler 在React 如何渲染的可以參考 React 第三十四章 React 渲染流程

下面我們根據流程圖先簡單的了解 Scheduler 的調度過程

image-20221230103504711

Scheduler 維護兩個隊列,分別存放普通任務和延時任務

  • taskQueue::普通任務
  • timerQueue:延時任務

Scheduler 接收的是普通任務時,會加入普通任務隊列,然后執行 requestHostCallback

schedulePerformWorkUntilDeadline 根據環境創建宏任務(主要創建的就是 MessageChannel),然后執行 performWorkUntilDeadline。該方法實際上主要就是在調用 scheduledHostCallback(flushWork),調用之后,返回一個布爾值,根據這個布爾值來判斷是否還有剩余的任務,如果還有,就是用 messageChannel 進行一個宏任務的包裝,放入到任務隊列里面。flushWork 主要是調用 wookLoop。workLoop 在當前貞中只要還有時間,就會不停的執行任務

Scheduler 接收的是延時任務時,會加入延時隊列,然后執行 requestHostTimout(主要是設置setTimeout),然后執行 handleTimeout,將 到時間的延時任務加入到 普通任務隊列,然后執行 requestHostTimout。接下來的操作就和普通任務隊列接下來的操作一致。

下面我們來看源碼的具體實現。Scheduler 的核心源碼位于 packages/scheduler/src/forks/Scheduler.js。 unstable_scheduleCallback就是我們要看到的 Scheduler

schedule調度普通任務

scheduleCallback 該函數的主要目的就是用調度任務,該方法的分析如下:

let getCurrentTime = () => performance.now();// 有兩個隊列分別存儲普通任務和延時任務
// 里面采用了一種叫做小頂堆的算法,保證每次從隊列里面取出來的都是優先級最高(時間即將過期)
var taskQueue = []; // 存放普通任務
var timerQueue = []; // 存放延時任務var maxSigned31BitInt = 1073741823;// Timeout 對應的值
var IMMEDIATE_PRIORITY_TIMEOUT = -1;
var USER_BLOCKING_PRIORITY_TIMEOUT = 250;
var NORMAL_PRIORITY_TIMEOUT = 5000;
var LOW_PRIORITY_TIMEOUT = 10000;
var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;/**** @param {*} priorityLevel 優先級等級* @param {*} callback 具體要做的任務* @param {*} options { delay: number } 這是一個對象,該對象有 delay 屬性,表示要延遲的時間* @returns*/
function unstable_scheduleCallback(priorityLevel, callback, options) {// 獲取當前的時間var currentTime = getCurrentTime();var startTime;// 整個這個 if.. else 就是在設置起始時間,如果有延時,起始時間需要添加上這個延時if (typeof options === "object" && options !== null) {var delay = options.delay;// 如果設置了延時時間,那么 startTime 就為當前時間 + 延時時間if (typeof delay === "number" && delay > 0) {startTime = currentTime + delay;} else {startTime = currentTime;}} else {startTime = currentTime;}var timeout;// 根據傳入的優先級等級來設置不同的 timeoutswitch (priorityLevel) {case ImmediatePriority:timeout = IMMEDIATE_PRIORITY_TIMEOUT;break;case UserBlockingPriority:timeout = USER_BLOCKING_PRIORITY_TIMEOUT;break;case IdlePriority:timeout = IDLE_PRIORITY_TIMEOUT;break;case LowPriority:timeout = LOW_PRIORITY_TIMEOUT;break;case NormalPriority:default:timeout = NORMAL_PRIORITY_TIMEOUT;break;}// 接下來就計算出過期時間// 計算出來的時間有些比當前時間要早,絕大部分比當前的時間要晚一些var expirationTime = startTime + timeout;// 創建一個新的任務var newTask = {id: taskIdCounter++, // 任務 idcallback, // 該任務具體要做的事情priorityLevel, // 任務的優先級別startTime, // 任務開始時間expirationTime, // 任務的過期時間sortIndex: -1, // 用于后面在小頂堆(這是一種算法,可以始終從任務隊列中拿出最優先的任務)進行排序的索引};if (enableProfiling) {newTask.isQueued = false;}if (startTime > currentTime) {// This is a delayed task.// 說明這是一個延時任務newTask.sortIndex = startTime;// 將該任務推入到 timerQueue 的任務隊列中push(timerQueue, newTask);if (peek(taskQueue) === null && newTask === peek(timerQueue)) {// 進入此 if,說明 taskQueue 里面的任務已經執行完畢了// 并且從 timerQueue 里面取出一個最新的任務又是當前任務// All tasks are delayed, and this is the task with the earliest delay.// 下面的 if.. else 就是一個開關if (isHostTimeoutScheduled) {// Cancel an existing timeout.cancelHostTimeout();} else {isHostTimeoutScheduled = true;}// Schedule a timeout.// 如果是延時任務,調用 requestHostTimeout 進行任務的調度requestHostTimeout(handleTimeout, startTime - currentTime);}} else {// 說明不是延時任務newTask.sortIndex = expirationTime; // 設置了 sortIndex 后,可以在任務隊列里面進行一個排序// 推入到 taskQueue 任務隊列push(taskQueue, newTask);if (enableProfiling) {markTaskStart(newTask, currentTime);newTask.isQueued = true;}// Schedule a host callback, if needed. If we're already performing work,// wait until the next time we yield.// 最終調用 requestHostCallback 進行任務的調度if (!isHostCallbackScheduled && !isPerformingWork) {isHostCallbackScheduled = true;requestHostCallback(flushWork);}}// 向外部返回任務return newTask;
}

該方法主要注意以下幾個關鍵點:

  • 關于任務隊列有兩個,一個 taskQueue,另一個是 timerQueue,taskQueue 存放普通任務,timerQueue 存放延時任務,任務隊列內部用到了小頂堆的算法,保證始終放進去(push)的任務能夠進行正常的排序,回頭通過 peek 取出任務時,始終取出的是時間優先級最高的那個任務
  • 根據傳入的不同的 priorityLevel,會進行不同的 timeout 的設置,任務的 timeout 時間也就不一樣了,有的比當前時間還要小,這個代表立即需要執行的,絕大部分的時間比當前時間大。

image-20221229145930771

  • 不同的任務,最終調用的函數不一樣
    • 普通任務:requestHostCallback(flushWork)
    • 延時任務:requestHostTimeout(handleTimeout, startTime - currentTime);

requestHostCallback 和 schedulePerformWorkUntilDeadline

/*** * @param {*} callback 是在調用的時候傳入的 flushWork* requestHostCallback 這個函數沒有做什么事情,主要就是調用 schedulePerformWorkUntilDeadline*/
function requestHostCallback(callback) {scheduledHostCallback = callback;// scheduledHostCallback ---> flushWorkif (!isMessageLoopRunning) {isMessageLoopRunning = true;schedulePerformWorkUntilDeadline(); // 實例化 MessageChannel 進行后面的調度}
}let schedulePerformWorkUntilDeadline; // undefined
if (typeof localSetImmediate === 'function') {// Node.js and old IE.// https://github.com/facebook/react/issues/20756schedulePerformWorkUntilDeadline = () => {localSetImmediate(performWorkUntilDeadline);};
} else if (typeof MessageChannel !== 'undefined') {// 大多數情況下,使用的是 MessageChannelconst channel = new MessageChannel();const port = channel.port2;channel.port1.onmessage = performWorkUntilDeadline;schedulePerformWorkUntilDeadline = () => {port.postMessage(null);};
} else {// setTimeout 進行兜底schedulePerformWorkUntilDeadline = () => {localSetTimeout(performWorkUntilDeadline, 0);};
}
  • requestHostCallback 主要就是調用了 schedulePerformWorkUntilDeadline
  • schedulePerformWorkUntilDeadline 一開始是 undefiend,根據不同的環境選擇不同的生成宏任務的方式

performWorkUntilDeadline

let startTime = -1;
const performWorkUntilDeadline = () => {// scheduledHostCallback ---> flushWorkif (scheduledHostCallback !== null) {// 獲取當前的時間const currentTime = getCurrentTime();// Keep track of the start time so we can measure how long the main thread// has been blocked.// 這里的 startTime 并非 unstable_scheduleCallback 方法里面的 startTime// 而是一個全局變量,默認值為 -1// 用來測量任務的執行時間,從而能夠知道主線程被阻塞了多久startTime = currentTime;const hasTimeRemaining = true; // 默認還有剩余時間// If a scheduler task throws, exit the current browser task so the// error can be observed.//// Intentionally not using a try-catch, since that makes some debugging// techniques harder. Instead, if `scheduledHostCallback` errors, then// `hasMoreWork` will remain true, and we'll continue the work loop.let hasMoreWork = true; // 默認還有需要做的任務try {// scheduledHostCallback ---> flushWork(true, 開始時間): boolean// 如果是 true,代表工作沒做完// false 代表沒有任務了hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);} finally {if (hasMoreWork) {// If there's more work, schedule the next message event at the end// of the preceding one.// 那么就使用 messageChannel 進行一個 message 事件的調度,就將任務放入到任務隊列里面schedulePerformWorkUntilDeadline();} else {// 說明任務做完了isMessageLoopRunning = false;scheduledHostCallback = null; // scheduledHostCallback 之前為 flushWork,設置為 null}}} else {isMessageLoopRunning = false;}// Yielding to the browser will give it a chance to paint, so we can// reset this.needsPaint = false;
};
  • 該方法實際上主要就是在調用 scheduledHostCallback(flushWork),調用之后,返回一個布爾值,根據這個布爾值來判斷是否還有剩余的任務,如果還有,就是用 messageChannel 進行一個宏任務的包裝,放入到任務隊列里面

flushWork 和 workLoop

/**** @param {*} hasTimeRemaining 是否有剩余的時間,一開始是 true* @param {*} initialTime 做這一個任務時開始執行的時間* @returns*/
function flushWork(hasTimeRemaining, initialTime) {// ...try {if (enableProfiling) {try {// 核心實際上是這一句,調用 workLoopreturn workLoop(hasTimeRemaining, initialTime);} catch (error) {// ...}} else {// 核心實際上是這一句,調用 workLoopreturn workLoop(hasTimeRemaining, initialTime);}} finally {// ...}
}/**** @param {*} hasTimeRemaining 是否有剩余的時間,一開始是 true* @param {*} initialTime 做這一個任務時開始執行的時間* @returns*/
function workLoop(hasTimeRemaining, initialTime) {let currentTime = initialTime;// 該方法實際上是用來遍歷 timerQueue,判斷是否有已經到期了的任務// 如果有,將這個任務放入到 taskQueueadvanceTimers(currentTime);// 從 taskQueue 里面取一個任務出來currentTask = peek(taskQueue);while (currentTask !== null &&!(enableSchedulerDebugging && isSchedulerPaused)) {if (currentTask.expirationTime > currentTime &&(!hasTimeRemaining || shouldYieldToHost())) {// This currentTask hasn't expired, and we've reached the deadline.// currentTask.expirationTime > currentTime 表示任務還沒有過期// hasTimeRemaining 代表是否有剩余時間// shouldYieldToHost 任務是否應該暫停,歸還主線程// 那么我們就跳出 whilebreak;}// 沒有進入到上面的 if,說明這個任務到過期時間,并且有剩余時間來執行,沒有到達需要瀏覽器渲染的時候// 那我們就執行該任務即可const callback = currentTask.callback; // 拿到這個任務if (typeof callback === "function") {// 說明當前的任務是一個函數,我們執行該任務currentTask.callback = null;currentPriorityLevel = currentTask.priorityLevel;const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;if (enableProfiling) {markTaskRun(currentTask, currentTime);}// 任務的執行實際上就是在這一句const continuationCallback = callback(didUserCallbackTimeout);currentTime = getCurrentTime();if (typeof continuationCallback === "function") {// If a continuation is returned, immediately yield to the main thread// regardless of how much time is left in the current time slice.// $FlowFixMe[incompatible-use] found when upgrading FlowcurrentTask.callback = continuationCallback;if (enableProfiling) {// $FlowFixMe[incompatible-call] found when upgrading FlowmarkTaskYield(currentTask, currentTime);}advanceTimers(currentTime);return true;} else {if (enableProfiling) {// $FlowFixMe[incompatible-call] found when upgrading FlowmarkTaskCompleted(currentTask, currentTime);// $FlowFixMe[incompatible-use] found when upgrading FlowcurrentTask.isQueued = false;}if (currentTask === peek(taskQueue)) {pop(taskQueue);}advanceTimers(currentTime);}} else {// 直接彈出pop(taskQueue);}// 再從 taskQueue 里面拿一個任務出來currentTask = peek(taskQueue);}// Return whether there's additional workif (currentTask !== null) {// 如果不為空,代表還有更多的任務,那么回頭外部的 hasMoreWork 拿到的就也是 truereturn true;} else {// taskQueue 這個隊列是空了,那么我們就從 timerQueue 里面去看延時任務const firstTimer = peek(timerQueue);if (firstTimer !== null) {requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);}// 沒有進入上面的 if,說明 timerQueue 里面的任務也完了,返回 false,回頭外部的 hasMoreWork 拿到的就也是 falsereturn false;}
}
  • flushWork 主要就是在調用 workLoop
  • workLoop 首先有一個 while 循環,該 while 循環保證了能夠從任務隊列中不停的取任務出來
 while (currentTask !== null &&!(enableSchedulerDebugging && isSchedulerPaused)){// ...}
  • 當然,不是說一直從任務隊列里面取任務出來執行就完事兒,每次取出一個任務后,我們還需要一系列的判斷
if (currentTask.expirationTime > currentTime &&(!hasTimeRemaining || shouldYieldToHost())) {break;
}
  • currentTask.expirationTime > currentTime 表示任務還沒有過期
  • hasTimeRemaining 代表是否有剩余時間
  • shouldYieldToHost 任務是否應該暫停,歸還主線程
  • 如果進入 if,說明因為某些原因不能再執行任務,需要立即歸還主線程,那么我們就跳出 while

shouldYieldToHost

function shouldYieldToHost() {// getCurrentTime 獲取當前時間// startTime 是我們任務開始時的時間,一開始是 -1,之后任務開始時,將任務開始時的時間復值給了它const timeElapsed = getCurrentTime() - startTime;// frameInterval 默認設置的是 5msif (timeElapsed < frameInterval) {// 主線程只被阻塞了一點點時間,遠遠沒達到需要歸還的時候return false;}// 如果沒有進入上面的 if,說明主線程已經被阻塞了一段時間了// 需要歸還主線程if (enableIsInputPending) {if (needsPaint) {// There's a pending paint (signaled by `requestPaint`). Yield now.return true;}if (timeElapsed < continuousInputInterval) {// We haven't blocked the thread for that long. Only yield if there's a// pending discrete input (e.g. click). It's OK if there's pending// continuous input (e.g. mouseover).if (isInputPending !== null) {return isInputPending();}} else if (timeElapsed < maxInterval) {// Yield if there's either a pending discrete or continuous input.if (isInputPending !== null) {return isInputPending(continuousOptions);}} else {// We've blocked the thread for a long time. Even if there's no pending// input, there may be some other scheduled work that we don't know about,// like a network event. Yield now.return true;}}// `isInputPending` isn't available. Yield now.return true;
}
  • 首先計算 timeElapsed,然后判斷是否超時,沒有的話就返回 false,表示不需要歸還,否則就返回 true,表示需要歸還。
  • frameInterval 默認設置的是 5ms

advanceTimers

function advanceTimers(currentTime) {// Check for tasks that are no longer delayed and add them to the queue.// 從 timerQueue 里面獲取一個任務let timer = peek(timerQueue);// 遍歷整個 timerQueuewhile (timer !== null) {if (timer.callback === null) {// 這個任務沒有對應的要執行的 callback,直接從這個隊列彈出pop(timerQueue);} else if (timer.startTime <= currentTime) {// 進入這個分支,說明當前的任務已經不再是延時任務// 我們需要將其轉移到 taskQueuepop(timerQueue);timer.sortIndex = timer.expirationTime;push(taskQueue, timer); // 推入到 taskQueue// ...} else {return;}// 從 timerQueue 里面再取一個新的進行判斷timer = peek(timerQueue);}
}
  • 該方法就是遍歷整個 timerQueue,查看是否有已經過期的方法,如果有,不是說直接執行,而是將這個過期的方法添加到 taskQueue 里面。

Scheduler調度延時任務

function unstable_scheduleCallback(priorityLevel,callback,options){//...if (startTime > currentTime) {// 調度一個延時任務requestHostTimeout(handleTimeout, startTime - currentTime);} else {// 調度一個普通任務requestHostCallback(flushWork);}
}
  • 可以看到,調度一個延時任務的時候,主要是執行 requestHostTimeout

requestHostTimeout

// 實際上在瀏覽器環境就是 setTimeout
const localSetTimeout = typeof setTimeout === 'function' ? setTimeout : null;/*** * @param {*} callback 就是傳入的 handleTimeout* @param {*} ms 延時的時間*/
function requestHostTimeout(callback, ms) {taskTimeoutID = localSetTimeout(() => {callback(getCurrentTime());}, ms);/*** 因此,上面的代碼,就可以看作是* id = setTimeout(function(){*    handleTimeout(getCurrentTime())* }, ms)*/
}

可以看到,requestHostTimeout 實際上就是調用 setTimoutout,然后在 setTimeout 中,調用傳入的 handleTimeout

handleTimeout

/**** @param {*} currentTime 當前時間*/
function handleTimeout(currentTime) {isHostTimeoutScheduled = false;// 遍歷timerQueue,將時間已經到了的延時任務放入到 taskQueueadvanceTimers(currentTime);if (!isHostCallbackScheduled) {if (peek(taskQueue) !== null) {// 從普通任務隊列中拿一個任務出來isHostCallbackScheduled = true;// 采用調度普通任務的方式進行調度requestHostCallback(flushWork);} else {// taskQueue任務隊列里面是空的// 再從 timerQueue 隊列取一個任務出來// peek 是小頂堆中提供的方法const firstTimer = peek(timerQueue);if (firstTimer !== null) {// 取出來了,接下來取出的延時任務仍然使用 requestHostTimeout 進行調度requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);}}}
}
  • handleTimeout 里面主要就是調用 advanceTimers 方法,該方法的作用是將時間已經到了的延時任務放入到 taskQueue,那么現在 taskQueue 里面就有要執行的任務,然后使用 requestHostCallback 進行調度。如果 taskQueue 里面沒有任務了,再次從 timerQueue 里面去獲取延時任務,然后通過 requestHostTimeout 進行調度。

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

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

相關文章

C++入門系列-賦值運算符重載

&#x1f308;個人主頁&#xff1a;羽晨同學 &#x1f4ab;個人格言:“成為自己未來的主人~” 賦值運算符重載 運算符重載 C為了增強代碼的可讀性引入了運算符重載&#xff0c;運算符重載是具有特殊函數名的函數&#xff0c;也具有其返回值類型&#xff0c;函數名字以及參…

使用React.createContext()在React應用中傳遞數據,nolan出品

React.createContext() 是React中的一個API&#xff0c;用于創建一個“上下文”&#xff0c;這是一種在組件樹中傳遞數據的方法&#xff0c;而無需手動將props逐級傳遞。 這個方法接受一個參數&#xff0c;即默認值&#xff0c;當組件在樹中上層沒有找到對應的Provider時&…

【GESP試卷】2024年03月Scratch三級試卷

2024年GESP03月認證Scratch三級試卷 分數&#xff1a;100 題數&#xff1a;27 一、單選題(共15題&#xff0c;每題2分&#xff0c;共30分) 010203040506070809101112131415CBACADDADCBCBAB 1、小楊的父母最近剛剛給他買了一塊華為手表&#xff0c;他說手表上跑的是鴻蒙&…

24長三角A題思路+分析選題

需要資料的寶子們可以進企鵝獲取 A題 問題1&#xff1a;西湖游船上掉落華為 mate 60 pro 手機 1. 手機掉落范圍分析 物品特征&#xff1a;華為 mate 60 pro 手機的尺寸、重量、形狀等特性。靜水假設&#xff1a;西湖水面平靜&#xff0c;不考慮水流影響。掉落位置&#xff…

在C#語言里對NULL的技術處理

文章目錄 前言1 、NULL合并操作符&#xff08;??&#xff09;2. 條件運算符 (?:)3. 空條件運算符(?.)4. 空合并賦值操作符 (??)5. 寬容運算符 (!.)6. 使用 is 運算符7. ArgumentNullException 參數空異常結論 前言 在 C# 中&#xff0c;null 是一個特殊存在&#xff0c;…

安卓Fragment基礎

目錄 前言一、基礎使用二、動態添加Fragment三、Fragment的生命周期四、Fragment之間進行通信五、Fragment兼容手機和平板示例 前言 Fragment基礎使用筆記 一、基礎使用 Activity布局和文件 <LinearLayout xmlns:android"http://schemas.android.com/apk/res/andro…

OpenAI 發布 GPT-4o,再次鞏固行業地位!

5 月 14 日凌晨 1 點&#xff08;太平洋時間上午 10 點&#xff09;&#xff0c;OpenAI 發布了其最新的 GPT-4o&#xff0c;再次鞏固了其在人工智能領域的領導地位。這次發布不僅僅是一個產品的推出&#xff0c;更是向世界宣告 AI 技術已邁入一個全新的紀元。OpenAI 的 CEO 薩姆…

品牌竄貨治理管控的方法

竄貨問題確實是一個需要品牌方高度關注和有效治理的難題。這種現象通常源于品牌區域銷售政策的差異&#xff0c;經銷商為了獲取更多的利潤&#xff0c;往往會利用這些差異進行跨區域的低價銷售。這不僅損害了大多數經銷商的利益&#xff0c;也破壞了市場的穩定和品牌價值。 品牌…

深入理解 Spring 循環依賴之三級緩存(附源碼分析)

前言&#xff1a; 學過 Spring 的都知道 Spring 利用三級緩存解決了循環依賴問題&#xff0c;那你知道什么是循環依賴&#xff1f;什么又是三級緩存&#xff1f;本篇將從源碼層面分析 Spring 是怎么去利用三級緩存幫我們解決循環依賴問題。 深入理解 Spring IOC 底層實現機制…

三生隨記——麗水詭事

在浙江的深山之中&#xff0c;隱藏著一座名為麗水的古老小城。這里山水秀麗&#xff0c;風景如畫&#xff0c;但在這美麗的外表下&#xff0c;卻隱藏著不為人知的恐怖秘密。 傳聞&#xff0c;麗水的郊外有一片被詛咒的竹林。這片竹林與其他竹林不同&#xff0c;它的葉子常年枯黃…

c# datagridview基本操作,包括行拖拽,添加自定義行列。

項目場景&#xff1a; 這段代碼定義了一個名為 ucDatagridviewHelper 的用戶控件&#xff08;UserControl&#xff09;&#xff0c;該控件包含了一個 DataGridView 控件和一些其他功能。 這段代碼的主要部分&#xff1a; 構造函數&#xff1a;在構造函數中&#xff0c;初始化…

C++ | Leetcode C++題解之第89題格雷編碼

題目&#xff1a; 題解&#xff1a; class Solution { public:vector<int> grayCode(int n) {vector<int> ret(1 << n);for (int i 0; i < ret.size(); i) {ret[i] (i >> 1) ^ i;}return ret;} };

數據結構--紅黑樹(RBTree)

一、紅黑樹概念 1.1 什么是紅黑樹 紅黑樹&#xff0c;是一種二叉搜索樹&#xff0c;但在每個結點上增加一個存儲位表示結點的顏色&#xff0c;可以是Red或 Black。 通過對任何一條從根到葉子的路徑上各個結點著色方式的限制&#xff0c;紅黑樹確保沒有一條路徑會比其他路徑長…

openEuler-22.03-LTS安裝opengauss5.0.1(包含cm集群管理)主備

環境說明 openEuler-22.0.3-LTS opengauss5.0.1 安裝數據庫 安裝系統依賴包 yum -y install lksctp* yum -y install psmisc yum -y install bzip2 yum -y install unzip yum -y install gcc yum -y install gcc-c yum -y install perl yum -y install libxml2-devel yum …

前端(包含cocosCreator)開發環節調取后端接口時跨域,解決辦法之反向代理

/** eslint-disable */ var http require(http),httpProxy require(http-proxy),HttpProxyRules require(http-proxy-rules);// Set up proxy rules instance var port 9090 var proxyRules new HttpProxyRules({rules: {/api/(.*): https://baidu.com/$1, // 測試環境游戲…

自學VBA 設置單元格文字格式 筆記

一.設定對應單元格對應需要顯示的格式 Cells(1, 1).Font.Size 18 字體大小 Cells(1, 2).Font.Color RGB(255, 0, 0) 字體顏色 Cells(1, 3).Font.Name "黑體" 字體類型 Cells(1, 4).Font.Italic True 字體斜體 Cells(1, 5).Font.FontStyle "BOLD"…

ubuntu下gcc編譯器的安裝

.gcc編譯器的安裝 一般linux下是覆蓋含有的&#xff0c;如果沒有執行更新命令 sudo apt update gcc安裝成功&#xff0c;可以檢查一下版本 可以看出我的gcc是9.4.0版本的

驗證torch.nn.Conv2d

import os import cv2 import torch import numpy as np import random import cv2 as cv from matplotlib import pyplot as pltdef f_VerifyConv2D():"""驗證torch.nn.Conv2d&#xff0c; 并將輸入數據及權重保存到txt文件中"""x torch.randn…

SpringBoot環境隔離Profiles

前言 通常我們開發不可能只有一個生產環境&#xff0c;還會有其它的開發&#xff0c;測試&#xff0c;預發布環境等等。為了更好的管理每個環境的配置項&#xff0c;springboot也提供了對應的環境隔離的方法。 直接上干貨 知識點 激活環境方法 1&#xff0c;在application…

專用設備制造業供應商收發文件,有什么專業而輕便的方式嗎?

專用設備制造業的特點包括&#xff1a;門類廣、跨度大、科技含量高。它主要生產的是國民經濟各部門&#xff08;包括采掘、化工、冶煉、能源、醫療衛生、環保等&#xff09;所急需的重大成套設備&#xff0c;例如礦產資源井采及露天開采設備、大型火電、水電、核電成套設備、石…