【Linux】System V消息隊列 System V信號量

在這里插入圖片描述

👦個人主頁:Weraphael
?🏻作者簡介:目前正在學習c++和算法
??專欄:Linux
🐋 希望大家多多支持,咱一起進步!😁
如果文章有啥瑕疵,希望大佬指點一二
如果文章對你有幫助的話
歡迎 評論💬 點贊👍🏻 收藏 📂 加關注😍


目錄

  • 前言
  • 一、消息隊列 (了解)
      • 1.1 原理
      • 1.2 消息隊列的數據結構
      • 1.3 系統調用接口
        • 1.3.1 msgget - 創建消息隊列
        • 1.3.2 msgctl - 釋放消息隊列
        • 1.3.3 msgsnd - 發送數據塊
        • 1.3.4 msgrcv - 接收數據塊
      • 1.4 小結
  • 二、信號量
      • 2.1 前置概念:互斥、臨界資源等概念(重點)
      • 2.2 理解信號量(重點)
      • 2.3 總結一波
      • 2.4 系統調用接口(了解)
        • 2.4.1 semget - 創建信號量
        • 2.4.2 semctl - 釋放
        • 2.4.3 semop - 操作
      • 2.5 信號量憑什么是進程間通信的一種?
      • 2.6 信號量的數據結構
  • 三、深入理解 System V 通信方式 (重點)

前言

System V通信標準中,還有一種通信方式:消息隊列,以及一種實現互斥的工具:信號量;隨著時代的發展,這些陳舊的標準都已經較少使用了,但作為IPC中的經典知識,我們可以對其做一個簡單了解。尤其是 信號量,可以通過它,為以后多線程學習中POSIX信號量的學習做鋪墊

一、消息隊列 (了解)

1.1 原理

進程間通信的本質是:要讓雙方進程看到同一塊資源。那么對于System V消息隊列,操作系統首先就要在內核中創建一個隊列(數據結構),再通過某種手段將兩個或多個進程看到同一個隊列后,即可通信

  • 進程A發送數據是以數據塊的形式發送到消息隊列中。
  • 進程B同樣是以數據塊的形發送到消息隊列中。
  • 注意:System V消息隊列允許多個進程雙向進行通信,而管道通常只能單向通信

在這里插入圖片描述

  • 但有一個問題:那消息隊列中存放著不同進程發送的數據塊,那如何判斷該數據塊是由哪個進程接收呢?

發送消息時,接收進程通常是根據消息類型來判斷消息的來源。

當然了,消息隊列跟共享內存一樣,是由操作系統創建的,其生命周期不隨進程,因此在使用結束后需要手動釋放,不然會導致內存泄漏!

1.2 消息隊列的數據結構

而我們知道,因為系統中不止一對進程在進行通信,可能會存在多個,那么操作系統就要在內核中開辟多個消息隊列,那么操作系統就必須對這些消息隊列進行管理,這又得搬出管理的六字真言:先描述,再組織。在Unix/Linux中,描述消息隊列的信息通常通過struct msqid_ds結構體來表示:

  • struct msqid_ds
struct msqid_ds
{// struct ipc_perm 結構包含了消息隊列的所有權和權限信息。struct ipc_perm msg_perm;  // 最后一次向隊列中發送消息 (msgsnd) 的時間。 time_t msg_stime;      // 最后一次從隊列中接收消息 (msgrcv) 的時間。     time_t msg_rtime;     // 消息隊列屬性最后一次變更的時間。       time_t msg_ctime;       // 隊列中當前的字節數     unsigned long __msg_cbytes;    // 隊列中當前的消息數目。                           msgqnum_t msg_qnum;         // 隊列中允許存放的最大字節數。                          msglen_t msg_qbytes;  // 最后一次發送消息 (msgsnd) 的進程pid。                  pid_t msg_lspid;    // 最后一次接收消息 (msgrcv) 的進程pid。pid_t msg_lrpid;           
};
  • struct ipc_perm
struct ipc_perm
{// __key用于標識 IPC 對象的鍵值,由用戶指定。key_t __key;         // 擁有者的有效用戶ID (UID),即對象的當前所有者。uid_t uid;          // 擁有者的有效組ID (GID),即對象的當前所屬組。gid_t gid;        // 創建者的有效用戶ID (UID),即創建對象的用戶。    uid_t cuid;     // 創建者的有效組ID (GID),即創建對象的用戶所屬的組。      gid_t cgid;        // 對象的權限模式,定義了對象的訪問權限,通常以八進制表示。    unsigned short mode;  // 序列號,用于處理 IPC 對象創建時的競爭條件。unsigned short __seq; 
};

最后再通過諸如鏈表、順序表等數據結構將這些結構體對象管理起來。因此,往后我們對共享內存的管理,只需轉化為對某種數據結構的增刪查改。

1.3 系統調用接口

1.3.1 msgget - 創建消息隊列

msgget用于創建一個新的System V消息隊列或獲取一個已經存在的消息隊列。

函數原型如下:

#include <sys/types.h>#include <sys/ipc.h>
#include <sys/msg.h>int msgget(key_t key, int msgflg);

參數說明:

  • key:消息隊列的鍵值。這個鍵值用于唯一標識一個消息隊列(內核層使用),多個進程可以通過相同的鍵值來訪問同一個消息隊列。通常,可以使用ftok函數來生成一個鍵值。

  • msgflg:這是一個標志參數,用于指定操作模式和權限。可以用操作符'|'進行組合使用。它可以是以下幾個標志的組合:

    • IPC_CREAT:這個選項單獨使用的話,如果申請的消息隊列不存在,則創建一個新的消息隊列;如果存在,獲取已存在的消息隊列。
    • IPC_EXCL: 一般配合IPC_CREAT一起使用(不單獨使用)。他主要是檢測共享內存是否存在,如果存在,則出錯返回;如果不存在就創建。確保申請的消息隊列一定是新的。
    • 權限標志:以與文件權限類似的方式指定消息隊列的訪問權限(例如0666表示所有用戶可讀寫)。
    • 但在獲取已存在的消息隊列時,可以設置為0
  • 返回值:

    • 成功時返回消息隊列標識符msqid。(操作系統內部分配的,提供給用戶層使用,類似于文件描述符fd
    • 失敗時返回 -1,并設置errno以指示錯誤原因。

看到這里,有沒有發現以上接口和創建共享內存段shmget函數非常的像啊,至于key和消息隊列標識符的區別這里就不再詳細介紹了,更多細節請參考:點擊跳轉

接下來我們簡單使用msgget函數創建消息隊列,并使用 ipcs -q指令查看系統維護的消息隊列的信息

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>using namespace std;const char *pathname = "/home/wj";
int proj_id = 'A';int main()
{// 使用ftok函數生成鍵值key_t key = ftok(pathname, proj_id);printf("key is 0x%x\n", key);// 創建消息隊列int msqid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);printf("msqid is %d\n", msqid);return 0;
}

【程序結果】

在這里插入圖片描述

由于我們還沒使用消息隊列進行通信,所以已使用字節used-bytes和消息數messages都是0

1.3.2 msgctl - 釋放消息隊列

如上我們可以看見,當進程結束后,我們還是能看到消息隊列在系統的相關信息。所以我們應該手動將其釋放,避免內存泄漏!

釋放的方法和共享內存一樣有兩種方法:

  • 方法一:使用以下指令
ipcrm -q <msqid>

在這里插入圖片描述

  • 方法二:使用系統調用接口

msgctl函數是用于控制消息隊列的函數之一,它允許程序員執行多種操作,如獲取消息隊列的屬性、設置消息隊列的屬性、刪除消息隊列等。

具體的函數原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);

參數說明:

  • msqid:消息隊列的標識符。即msgget函數的返回值。

  • cmd:要執行的操作命令,可以是以下幾種之一:

    • IPC_STAT:獲取消息隊列的狀態信息,并將其存儲在struct msqid_ds *buf中。
    • IPC_SET:設置消息隊列的狀態,從struct msqid_ds *buf中讀取新的狀態信息。
    • IPC_RMID:從系統中刪除消息隊列。
  • buf:一個指向struct msqid_ds結構的指針,用于存儲或傳遞消息隊列的狀態信息。如果是刪除消息隊列,此參數可以設置為nullptr

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>using namespace std;const char *pathname = "/home/wj";
int proj_id = 'A';int main()
{// 使用ftok函數生成鍵值key_t key = ftok(pathname, proj_id);printf("key is 0x%x\n", key);// 創建消息隊列int msqid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);printf("msqid is %d\n", msqid);// 進程結束前釋放消息隊列msgctl(msqid, IPC_RMID, nullptr);return 0;
}

【程序結果】

在這里插入圖片描述

1.3.3 msgsnd - 發送數據塊

共享內存會比消息隊列多兩步:掛接到各自進程的進程地址空間、取消掛接。而對于消息隊列,當我們創建好資源后,就可以直接發送數據了。

msgsnd函數用于向消息隊列中發送消息,其函數原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

參數說明:

  • msqid:消息隊列的標識符,由msgget函數返回。
  • msgp:指向要發送的消息內容的指針,通常是用戶定義的結構體指針。就是我們在原理部分說的數據塊結構體。其結構如下:
struct msgbuf
{long mtype;    /* message type, must be > 0 */char mtext[1]; /* message data */
};

mtype就是傳說中數據塊類型,據發送方而設定;而mtex是一個比較特殊的東西:柔性數組,其中存儲待發送的 信息,因為是 柔性數組,所以可以根據 信息 的大小靈活調整數組的大小。對于柔性數組,大家可以參考這篇文章:點擊跳轉

  • msgsz:消息的大小,以字節為單位。這個大小必須是消息隊列的最大消息大小(msg_qbytes)的一個有效值,否則會導致msgsnd失敗。
  • msgflg:表示發送數據塊的方式,一般默認為0
  • 返回值:成功返回0,失敗返回-1
1.3.4 msgrcv - 接收數據塊

msgrcv函數用于從消息隊列中接收消息。

其函數原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

參數說明:

  • msqid:是消息隊列的標識符,由msgget函數返回。
  • msgp:是一個指向接收消息的緩沖區的指針,通常是一個用戶定義的結構體指針。
  • msgsz:是接收緩沖區的大小,即可以接收的最大消息大小(字節數)。如果實際接收到的消息大小大于msgsz,則消息可能會被截斷,這取決于msgflg是否設置了MSG_NOERROR
  • msgtyp:是消息類型,即從消息隊列中選擇接收的消息類型。如果msgtyp大于0,則只接收msgtyp 類型的消息;如果msgtyp等于0,則接收隊列中的第一個消息;如果msgtyp小于0,則接收隊列中小于或等于msgtyp絕對值的最高優先級的消息。
  • msgflg:表示接收數據塊的方式,一般默認為0
  • 返回值:成功返回接收到的消息的大小(字節數);失敗返回-1,并設置errno來指示錯誤的具體原因。

同樣的,接收的數據結構如下所示,也包含了類型和柔性數組

struct msgbuf
{long mtype;    /* message type, must be > 0 */char mtext[1]; /* message data */
};

1.4 小結

消息隊列 的大部分接口都與 共享內存 近似,所以掌握 共享內存 后,即可快速上手 消息隊列。但是如你所見,System V版的消息隊列 使用起來比較麻煩,并且過于陳舊,現在已經較少使用了,所以我們不必對其進行深究,知道個大概就行了 ~

二、信號量

2.1 前置概念:互斥、臨界資源等概念(重點)

進程A發送消息,進程B接收消息,在整個通信的過程中可能會出現錯亂問題。比方AB發送100Byte的任務信息,但是A可能才寫到50ByteB進程就開始讀走了,導致B進程任務信息不完整。我們稱之 數據不一致問題。因此,就衍生出以下幾個概念:

  1. 首先可以通過加鎖的方式(多線程部分講解) 來保證 互斥互斥本質就是:任何時刻,只允許一個執行流訪問共享資源(保護共享資源免受并發訪問的影響)
  2. 而這種只允許一個執行流訪問(執行訪問代碼)的資源稱做臨界資源。這個臨界資源一般是內存空間。(比方說管道就是一種臨界資源)
  3. 我們訪問臨界資源的代碼稱做 臨界區(類比代碼區)

注意:在管道通信中不存在這些問題,因為管道有原子性和同步互斥,而共享內存是沒有任何的保護機制的。

那么現在就可以解釋一個現象:為什么多個進程(或者線程)向顯示器打印各自的信息有時候會錯亂。原因很簡單,在Linux中,顯示器是文件,當多個進程向同一個文件打印,前提是這些進程需要看到同一份資源,所以顯示器文件在多進程中就是一個共享資源,而在打印的過程中并沒有添加保護機制,因此會看到數據不一致,錯亂問題。如果不想有這些情況,你就需要將顯示器文件變成臨界資源,如加鎖等。

2.2 理解信號量(重點)

信號量(有的教材叫信號燈)的本質是就是計數器。這個計數器用來記錄可用資源的數量

下面將一個故事來加深理解:假設一個放映廳有100個位置,對應也會售賣100張票(不考慮其他情況)。當我們買票去看電影,但是還沒去觀看(甚至不看),那個位置在電影的時間段永遠是我們自己的。因此,買票的本質是對資源的預定機制!而每賣一張票,剩余的票數(計數器)就要減一,對應的放映廳里面的資源就要少一個。當票數的計數器減到0之后,表示資源已經被申請完畢了。

臨界資源(如同放映廳)可以被劃分很多小塊的資源(如同放映廳里的位置),那么我們可以允許多個執行流(如同觀眾)來訪問這份臨界資源,但是最怕多個執行流會訪問同一個小塊的資源,一旦出現,就會發生混亂現象。因此,最好的方法就是引入一個計數器cnt,當cnt > 0 && cnt - 1,說明執行流申請資源成功(本質是對資源的預定機制),就有訪問資源的權限。當cnt <= 0表示資源被申請完了,當再有執行流申請,一定會失敗,除了有執行流釋放(退票)。

所以每一個執行流若是要訪問共享資源中的一小部分的時候,不是直接訪問,而是先申請計數器資源。如同看電影需要先買票 ~

故事還沒完,如果電影院的放映廳只有一個座位,我們只需要值為1的計數器,但如果有10個人想要這一個位置,那么必定要先申請計數器資源,但不變的是只有一個人能看電影,不就是只有一個執行流在訪問一份臨界資源,這不就是互斥訪問嗎?

在并發編程中,一個只能取兩個狀態(通常是01)的計數器被稱為二元信號量。二元信號量通常被用來實現互斥訪問(本質就是一個鎖),即保證在任何時刻只有一個進程(或線程)能夠訪問臨界資源。在電影院座位的故事中,計數器的兩個狀態可以分別表示座位的空閑(1)和已占用(0)狀態。

這又有一個新的問題:要訪問臨界資源,先要申請計數器資源。而信號量本質是計數器,那么信號量不就是共享資源嗎?

而計數器(信號量)作為保護方,要保護臨界資源只允許一個執行流訪問。但俗話說得好,要保護別人的安全,首先得先保證自己的安全。而對一個整數--其實并不安全,這里簡單說說為什么不是安全的,等到線程部分再詳談。

--操作在C語言上是一條語句;但是這條語句在匯編語言上就是多條匯編語句,一般是三條。首先計數器的值要從內存中放到CPU的寄存器中,然后再CPU進行--操作,最后再將計算結果協會計數器變量的內存位置。而執行流在運行的時候,可以隨時被切換,如果沒有適當的同步措施(如互斥鎖),多個執行流同時訪問計數器可能會導致競態條件。競態條件會破壞計數器的預期行為,使其不能正確地反映實際資源的狀態。

即然--都不安全,那談何保護別人?

因此,申請信號量,本質是對計數器--,在信號量中專門封裝了一個操作(方法),我們將這種操作稱為P操作。如果減一后的計數器值小于零(即信號量的計數器值變為負數),那么執行流就會被阻塞,直到信號量的計數器變為正數,表示有可用資源;釋放資源的同時也要釋放信號量,本質是對計數器進行++操作,也叫做V操作。

需要注意的是,P操作和V操作通常需要具有 原子性其意思是一件事情要么不做,要做就做完,是兩態的。沒有“正在做”這樣的概念。也就是說,原子性確保了多個執行流在執行--操作時,不會被其他執行流中斷或干擾,而且操作要么完全執行成功,要么完全不執行,沒有正在執行的說法。

2.3 總結一波

  1. 信號量本質是計數器,PV操作具有原子性。

  2. 執行流申請資源,必須先申請信號量(計數器)資源,得到信號量之后,才能訪問臨界資源!

  3. 信號量值10兩態的。二元信號量,就是互斥功能。

  4. 申請計數器(信號量)的本質是對臨界資源的預定機制!

2.4 系統調用接口(了解)

信號量的系統調用挺“惡心”的,大家了解就行~

2.4.1 semget - 創建信號量
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);
組成部分含義
返回值 int創建成功返回信號量集的 semid,失敗返回 -1
參數1 key_t key創建信號量集時的唯一 key 值,通過函數 ftok 計算獲取
參數2 int nsems待創建的信號量個數,這也正是 集 的來源
參數3 int semflg位圖,可以設置消息隊列的創建方式及創建權限

除了參數2,其他基本與另外倆兄弟一模一樣,實際傳遞時,一般傳 1,表示只創建一個 信號量

2.4.2 semctl - 釋放

老生常談的兩種釋放方式:指令釋放、函數釋放

  • 指令釋放:直接通過指令ipcrm -s <semid>釋放信號量集。你還可以使用ipcs -s來查看系統中信號量的相關信息。
  • 通過函數釋放。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);
組成部分含義
返回值 int成功返回 0,失敗返回 -1
參數1 int semid待控制的信號量集 id
參數2 int semnum表示對信號量集中的第 semnum 個信號量作操作
參數4 ...可變參數列表,不止可以獲取信號量的數據結構,還可以獲取其他信息
2.4.3 semop - 操作

信號量的操縱比較ex,也比較麻煩,所以僅作了解即可

使用 semop 函數對 信號量 進行諸如 +1、-1 的基本操作。

 #include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semop(int semid, struct sembuf *sops, unsigned nsops);
組成部分含義
返回值 int成功返回 0,失敗返回 -1
參數1 int semid待操作的信號量集 id
參數2 struct sembuf *sops一個比較特殊的參數,需要自己設計結構體
參數3 unsigned nsops可以簡單理解為信號量編號

重點在于參數2,這是一個結構體,具體成員如下:

unsigned short sem_num;  /* semaphore number */
short          sem_op;   /* semaphore operation */
short          sem_flg;  /* operation flags */

其中包含信號量編號、操作等信息,需要我們自己設計出一個結構體,然后傳給semop函數使用。

可以簡單理解為:sem_op 就是要進行的操作,如果將 sem_op 設為 -1,表示信號量 -1(申請),同理 +1 表示信號量 +1(歸還)

sem_flg 是設置動作,一般設為默認即可

當然這些函數我們不必深入去研究,知道個大概就行了

2.5 信號量憑什么是進程間通信的一種?

講了這么多信號量的知識,我們并沒有發現信號量能傳數據進行通信,而是作為一個計數器。

這里就要解釋一下了,通信并不僅僅在于數據的傳遞,也在于雙方互相協同

補充什么是協同:雙方或多方在通信或合作過程中,通過相互配合、相互支持、相互理解和相互作用,共同達成某種目標。

雖然協同不是以傳輸數據為目的,但是它是以事件通知為目的,它的本質也是在傳遞信息,只是沒那么容易感知到而已。

因此,協同本質也是通信,信號量首先要被所有的通信進程看到。

2.6 信號量的數據結構

Linux中,可以通過man semctl進行查看

  • struct semid_ds
struct semid_ds
{struct ipc_perm sem_perm; /* Ownership and permissions */time_t sem_otime;         /* Last semop time */time_t sem_ctime;         /* Last change time */unsigned long sem_nsems;  /* No. of semaphores in set */
};
  • struct ipc_perm
struct ipc_perm
{key_t __key;          /* Key supplied to semget(2) */uid_t uid;            /* Effective UID of owner */gid_t gid;            /* Effective GID of owner */uid_t cuid;           /* Effective UID of creator */gid_t cgid;           /* Effective GID of creator */unsigned short mode;  /* Permissions */unsigned short __seq; /* Sequence number */
};

顯然,無論是 共享內存、消息隊列、信號量,它們的ipc_perm結構體中的內容都是一模一樣的,結構上的統一可以帶來管理上的便利,具體原因可以接著往下看。

三、深入理解 System V 通信方式 (重點)

接下來我們再來詳細說說IPC資源在內核中是怎么管理的。

在這里插入圖片描述

如上我們發現:操作系統描述IPC對象(共享內存、消息隊列、信號量)的數據結構的第一個字段的第一個成員都是struct ipc_perm類型成員變量

這樣設計的好處就是,在操作系統內可以定義一個struct ipc_perm類型的數組(或鏈表等數據結構)來管理所有的IPC對象,此時每當我們申請一個IPC資源,就在該數組當中開辟一個這樣的結構。

在這里插入圖片描述

這是因為IPC對象的增、刪、查、改操作與struct ipc_perm結構體相關,struct ipc_perm包含了IPC對象的權限信息。這些權限信息對于操作系統來說是非常重要的,它決定了哪些進程可以訪問、操作這些IPC對象。因此,往后我們對IPC對象的增、刪、查和改操作,就轉化為對數組的增、刪、查和改操作。而數組下標,就是IPC對象的標識符。(類似于文件描述符fd

就比方說通過共享內存段標識符在數組中找到其struct ipc_perm對象,而當我們需要訪問其struct shmid_ds成員變量時,只需將struct ipc_perm*強制轉化為struct shmid_ds*即可訪問。

而操作系統為什么能知道要轉化哪個IPC對象?可以這么理解:

  • 在用戶角度,操作(增、刪、查、改)IPC對象時會使用struct ipc_perm結構體來描述對象的權限和所有者信息。這是給開發者和應用程序使用的接口,用來傳遞創建和訪問IPC對象的參數。
  • 但從內核角度出發,真正管理IPC對象的是kern_ipc_perm結構體(或類似的結構體)。內核會在創建IPC對象時使用特定的系統調用(如 msggetshmgetsemget)來分配和初始化相應的 kern_ipc_perm結構體。這些結構體通常包含一個類型標志位字段,用于標識這個IPC對象的類型。

那這不就是多態的思想嗎?struct ipc_perm充當基類,其他的IPC對象數據結構充當子類,它們繼承了 struct ipc_perm的屬性,并且增加了特定于每種 IPC 對象類型的信息和操作。指針指向誰就調用誰。

至此,進程間通信的知識點就到此結束啦~

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

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

相關文章

前端 JS 經典:小數運算不精確

原因&#xff1a;計算機對小數的存儲是二進制的&#xff0c;有限位數的二進制做算法得到的是有限位數&#xff0c;無限位數的二進制做運算&#xff0c;得到的是無限位數。 如下&#xff1a;0.5 和 0.25 轉二進制是有限位數&#xff0c;0.3 和 0.2 轉二進制是無限位數。 (0.5)…

Spark調度底層執行原理詳解(第35天)

系列文章目錄 一、Spark應用程序啟動與資源申請 二、DAG&#xff08;有向無環圖&#xff09;的構建與劃分 三、Task的生成與調度 四、Task的執行與結果返回 五、監控與容錯 六、優化策略 文章目錄 系列文章目錄前言一、Spark應用程序啟動與資源申請1. SparkContext的創建2. 資…

力扣1111.有效括號的嵌套深度

力扣1111.有效括號的嵌套深度 棧模擬 對于每個括號求出深度 奇數深度存入A&#xff0c;偶數深度存入B這樣最大程度降低最大深度 class Solution {public:vector<int> maxDepthAfterSplit(string s) {//因為棧中只會存(的數量 所有用一個變量即可int d 0;vector<i…

Python | Leetcode Python題解之第233題數字1的個數

題目&#xff1a; 題解&#xff1a; class Solution:def countDigitOne(self, n: int) -> int:# mulk 表示 10^k# 在下面的代碼中&#xff0c;可以發現 k 并沒有被直接使用到&#xff08;都是使用 10^k&#xff09;# 但為了讓代碼看起來更加直觀&#xff0c;這里保留了 kk,…

C語言內存管理深度解析面試題及參考答案(2萬字長文)

在嵌入式面試時,C語言內存管理是必問面試題,也是難點,相關知識點可以參考: C語言內存管理深度解析??????? 下面整理了各種類型的C語言內存管理的面試題: 目錄 全局變量和局部變量在內存中分別存儲在哪個區域? 靜態變量和全局變量有什么區別? 什么是作用域?…

ORM Bee,如何使用Oracle的TO_DATE函數?

ORM Bee,如何使用Oracle的TO_DATE函數? 在Bee V2.4.0,可以這樣使用: LocaldatetimeTable selectBeannew LocaldatetimeTable();Condition conditionBF.getCondition();condition.op("localdatetime", Op.ge, new TO_DATE("2024-07-08", "YYYY-MM-DD&…

如何選擇適合的分布式鎖技術

1. Redis鎖 優勢&#xff1a; 性能高&#xff1a;Redis作為內存數據庫&#xff0c;讀寫速度非常快&#xff0c;因此Redis鎖在性能上表現優異。 實現方便&#xff1a;Redis提供了豐富的命令集&#xff0c;可以方便地實現分布式鎖的邏輯。 劣勢&#xff1a; 可靠性&#xff1a…

Excel第31享:基于left函數的截取式數據裂變

1、需求描述 如下圖所示&#xff0c;在“Excel第30享”中統計2022年YTD各個人員的“上班工時&#xff08;a2&#xff09;”&#xff0c;需要基于工時明細表里的“日期”字段建立輔助列&#xff0c;生成“年份”字段&#xff0c;本文說明“年份”字段是怎么裂變而來的。 下圖為…

systemverilog的關聯數組

關聯數組定義 在 SystemVerilog 中&#xff0c;關聯數組&#xff08;Associative Arrays&#xff09;是一種非常靈活的數據結構&#xff0c;它可以使用任意類型的鍵&#xff08;key&#xff09;來索引數組中的元素。這使得關聯數組特別適合于實現類似哈希表&#xff08;hash t…

圖像處理:使用 OpenCV-Python 卡通化你的圖像(2)

一、說明 在圖像處理領域&#xff0c;將圖像卡通化是一種新趨勢。人們使用不同的應用程序將他們的圖像轉換為卡通圖像。如今&#xff0c;玩弄圖像是許多人的愛好。人們通常會點擊圖片并添加濾鏡或使用不同的東西自定義圖像并將其發布到社交媒體上。但我們是程序員&#xff0c;…

后端老鳥的前端初探:心得與領悟20240713

&#x1f389; 后端老鳥的前端初探&#xff1a;心得與領悟 &#x1f680; 作為一名深耕后端多年的開發者&#xff0c;我最近踏上了前端探索的征程。這段跨界之旅讓我有了許多深刻的心得與領悟&#xff0c;現在我想和大家細細分享&#xff1a; 前端技術的飛速更新 &#x1f68…

godis源碼分析——Redis協議解析器

前言 redis這個目錄下的所有代碼就是為了一個事情&#xff0c;就是適配redis。 流程 redis下的基本流程 源碼 在redis/client/client.go 主要是客戶端處理 package clientconst (created iotarunningclosed )type B struct {data chan stringticker *time.Ticker }// …

Docker安裝RabbitMQ(帶web管理端)

1.拉取帶web管理的鏡像 可以拉取rabbitmq對應版本的web管理端&#xff0c;比如&#xff1a;rabbitmq:3.9.11-management&#xff0c;也可以直接拉取帶web管理端的最新版本 rabbitmq:management. docker pull rabbitmq:3.9.11-management 注意&#xff1a;如果docker pull ra…

sqlalchemy使用with_entities返回指定數據列

sqlalchemy使用with_entities返回指定數據列 在 SQLAlchemy 中,with_entities 方法用于指定查詢語句返回的實體(Entity)或列(Column)。它允許你限制查詢的返回結果,只包含你感興趣的特定字段或實體 使用方法 假設有一個名為 User 的 SQLAlchemy 模型類,包含以下字段:…

Unity3D中如何降低游戲的Drawcall詳解

在Unity3D游戲開發中&#xff0c;Drawcall是一個至關重要的性能指標&#xff0c;它指的是CPU通知GPU繪制一個物體的命令次數。過多的Drawcall會導致游戲性能下降&#xff0c;因此優化Drawcall的數量是提高游戲性能的關鍵。本文將詳細介紹Unity3D中降低Drawcall的幾種主要方法&a…

設計模式使用場景實現示例及優缺點(行為型模式——模板方法模式)

模板方法模式&#xff08;Template Method Pattern&#xff09; 模板方法模式&#xff08;Template Method Pattern&#xff09;是一種行為設計模式&#xff0c;它定義了一個操作中的算法的骨架&#xff0c;將算法的一些步驟延遲到子類中。這樣可以在不改變算法的結構的前提下…

Git使用介紹教程

Git使用介紹教程 小白第一次寫博客,內容寫的可能不是很詳細,僅供參考,大家一起努力 gitee網址:https://gitee.com 大部分的開發團隊都以 Git 作為自己的版本控制工具,需要對 Git 的使用非常的熟悉。這篇文章中本人整理了自己在開發過程中經常使用到的 Git 命令,方便在偶…

jenkins系列-06.harbor

https://github.com/goharbor/harbor/releases?page2 https://github.com/goharbor/harbor/releases/download/v2.3.4/harbor-offline-installer-v2.3.4.tgz harbor官網&#xff1a;https://goharbor.io/ 點擊 Download now 鏈接&#xff0c;會自動跳轉到上述github頁面&am…

C++ | Leetcode C++題解之第233題數字1的個數

題目&#xff1a; 題解&#xff1a; class Solution { public:int countDigitOne(int n) {// mulk 表示 10^k// 在下面的代碼中&#xff0c;可以發現 k 并沒有被直接使用到&#xff08;都是使用 10^k&#xff09;// 但為了讓代碼看起來更加直觀&#xff0c;這里保留了 klong l…

Redis系列命令更新--Redis哈希命令

一、設置密碼驗證&#xff1a; 使用文本編輯器&#xff0c;這里使用Notepad&#xff0c;打開Redis服務配置文件。 注意&#xff1a;不要找錯了&#xff0c;通常為redis.windows-service.conf&#xff0c;而不是redis.windows.conf。后者是以非系統服務方式啟動程序使用的配置…