主頁 >  其他 > CSAPP 并發編程讀書筆記

CSAPP 并發編程讀書筆記

2021-12-21 06:23:29 其他

CSAPP 并發編程筆記

并發和并行

  • 并發:Concurrency,只要時間上重疊就算并發,可以是單處理器交替處理
  • 并行:Parallel,屬于并發的一種特殊情況(真子集),多核/多 CPU 同時處理

構造并發程式的方法

現代作業系統提供了 3 種基本的構造并發程式的方法:

  • 行程:每個邏輯控制流都是一個行程,由內核調度和維護,
  • I/O 多路復用 :在一個行程背景關系中顯式地調度他們自己的邏輯流,邏輯流被模型化為狀態機
  • 執行緒:運行在單一行程背景關系中的邏輯流,由內核進行調度,可以看作上面兩種方式的混合體(內核調度,但共享同一虛擬地址空間),

12.1 基于行程的并發編程

示例代碼

void sigchld_handler(int sig)
{
    while(waitpid(-1, 0, WNOHANG) > 0) 
        ;
}

int main()
{
    signal(SIGCHLD, sigchld_handler); // 回收僵死子行程資源
    listenfd = open_listenfd();
    while (1) {
      connfd = accept(...);
      if (fork() == 0) { // 子行程
        close(listenfd); // 關閉父行程 fd,不關閉問題不大,子行程結束時自動關閉
        process(connfd);
        close(connfd);
        exit(0);
      }
      close(connfd); // 父行程關閉 fd,重要!否則永遠不會釋放 connfd 連接描述符,導致記憶體泄漏!
    }
}

父、子行程中的已連接描述符都指向同一個檔案表表項,所以父行程關閉 connfd 至關重要,否則,永遠不會釋放已連接描述符 4 connfd 的檔案表條目,引起記憶體泄漏,因為 socket 檔案表表項中的參考計數,直到父子行程 connfd 都關閉了,到客戶端的連接才會終止,

行程并發優缺點

共享檔案表,但不共享地址空間(是優點,也是缺點),不方便共享資料,只能通過顯式 IPC,行程控制和 IPC 開銷大,

12.2 基于 I/O 多路復用的并發編程

背景知識

通過 select 函式,等待一組描述符 ready,

#include <sys/select.h>
int select(int n, fd_set *fdset, NULL, NULL, NULL); // 回傳 ready fd 的個數,出錯回傳 -1

FD_ZERO(fd_set *fdset);
FD_CLR(int fd, fd_set *fdset);
FD_SET(int fd, fd_set *fdset);
FD_ISSET(int fd, fd_set *fdset);

select 阻塞,直到至少一個 fd ready(即讀取一個位元組不阻塞)

示例代碼

注意:select 有副作用,會修改入參 fdset 的內容!

fd_set read_set;
FD_ZERO(&read_set);
FD_SET(STDIN_FILENO, &read_set);
FD_SET(listenfd, &read_set);

while(1) {
  fd_set ready_set = read_set; // 因為 select 會修改入參 read_set 的內容,每次都重新從 read_set 拷貝!
  select(listenfd+1, &ready_set, NULL, NULL, NULL);
  if(FD_ISSET(STDIN_FILENO, &ready_set)
     // ...
  if(FD_ISSET(listenfd, &ready_set)
     // ...
}

I/O 多路復用優缺點

  • 單一行程背景關系,共享資料容易,

  • 事件驅動,不需要背景關系切換,高效,有明顯的性能優勢,

  • 編碼復雜

  • 不能充分利用多核處理器

因為明顯的性能優勢,現代高性能服務器如 Node.js、nginx 和 Tornado 都是基于 I/O 多路復用的事件驅動

12.3 基于執行緒的并發編程

背景知識

# include <pthread.h>
typedef void *(func)(void *);

// 創建執行緒
int pthread_create(pthread_t *tid, pthread_attr_t *attr, func *f, void *arg);
// 回傳呼叫者執行緒 ID
pthread_t pthread_self();
// 顯示終止執行緒(threadFunc 回傳即隱式終止),如果主執行緒呼叫,則等待所有其他對等執行緒終止,再終止主執行緒和整個行程
void pthread_exit(void *thread_return);
// 對等執行緒以當前執行緒 ID 作為引數,呼叫 pthread_cancel 來終止當前執行緒
int pthread_cancel(pthread_t tid); // 成功回傳 0
// 回收已終止執行緒的資源
int pthread_join(pthread_t tid, void **thread_return); // 阻塞直到 tid 終止,成功回傳 0
// 分離執行緒
int pthread_detach(pthread_t tid); // 成功回傳 0
// 初始化執行緒 (可以用來實作單例模式)
pthread_once_t once = PTHREAD_ONCE_INIT; // 必須是全域或者靜態變數,固定初始化為 PTHREAD_ONCE_INIT(主要用其地址)
int pthread_once(pthread_once_t *once_control, void(*init_routine)(void));

執行緒由內核自動調度,每個執行緒有自己的執行緒背景關系

執行緒背景關系:執行緒 ID(TID)、堆疊、堆疊指標、程式計數器、通用目的暫存器和條件碼,

示例代碼

while (1) {
  pConnfd = new int();
  *pConnfd = accept(...);
  pthread_create(&tid, NULL, threadFunc, pConnfd);
}

void* threadFunc(void* p)
{
  int connfd = *p;
  pthread_detach(pthread_self());
  free(p);
}

為了避免 race condition,connfd 必須在堆中創建,在執行緒中釋放,而不能直接把 connfd 的地址傳給 threadFunc!

12.4 多執行緒共享變數

執行緒記憶體模型

  • 每個執行緒有獨立的執行緒背景關系,共享行程背景關系其余部分,包括整個用戶虛擬地址空間:只讀文本(代碼.text)、讀/寫資料(.bss & .data)、堆、共享庫代碼和資料,
  • 執行緒堆疊不對其他執行緒設防,

將變數映射到記憶體

  • 全域變數:定義在函式之外,僅一個實體@虛擬記憶體讀/寫區
  • 本地自動變數:定義在函式內,且沒有 static,@虛擬記憶體執行緒堆疊
  • 本地靜態變數:定義在函式內,并有 static,僅一個實體@虛擬記憶體讀/寫區

C++11 thread_local 存在哪里?

12.5 信號量

背景知識

  • cnt++ 可以細分 3 個子步驟:加載 L、更新 U、儲存 S,這三個動作必須一次性完成,不可中斷,
  • 進度圖不適用于多處理器,
  • P(s):若 s 非零,則 s 減 1,立即回傳,若 s 為零,掛起執行緒,直到 s 變為非零,然后將 s 減 1 回傳,
  • V(s):將 s 加 1,如果有執行緒阻塞,則喚醒這些執行緒中的某一個,
  • P、V 的加一減一的操作都是原子操作,即 L、U、S 的程序沒有中斷,
  • 如果有多個執行緒再等待喚醒,V(s) 只能隨機喚醒一個執行緒,不能指定喚醒哪個執行緒,
#include <semaphore.h>

// 成功回傳 0,出錯回傳 -1
int sem_init(sem_t *sem, 0, unsigned int value);
int sem_wait(sem_t *sem);  // P(s) 如果 s 為 0,掛起,直到 s 變為非零,V 操作可以重啟這個執行緒,
int sem_post(sem_t *sem);  // V(s)

生產者-消費者

int buf[N]; // 理解成 queue<int>
sem_t mutex, slots, items;

void init(int n)
{
    sem_init(&mutex, 0, 1);
    sem_init(&slots, 0, n);
    sem_init(&items, 0, 0);
}

void producer(T item)
{
    sem_wait(&slots)
    sem_wait(&mutex);
    // insert item into buf
    sem_post(&mutex);
    sem_post(&items);
}

T consumer()
{
    T item;
    sem_wait(&items);
    sem_wait(&mutex);
    // pop item from buf
    sem_post(&mutex);
    sem_post(&slots);
    return item;
}

讀者-寫者

讀者、寫者平等地爭奪 w,一旦讀者獲取了 w,將一直占有 w,直到最后一個讀者離開,釋放 w,

如果讀者不斷到達,寫者可能無限等待,導致饑餓

以下是一個讀者優先的例子,(弱優先級,當最后一個讀者釋放 w,下一個獲取 w 的不一定是等待 w 的讀者,也有可能是等待 w 的寫者!)

int readcnt = 0;
sem_t mutex; // 保護 readcnt
sem_t w;     // 讀者或寫者搶占 w

void init()
{
    sem_init(&mutex, 0, 1);
    sem_init(&w, 0, 1);
}

void reader()
{
    while(1)
    {
        sem_wait(&mutex);
        readcnt++;
        if(readcnt == 1)
            sem_wait(&w); // 如果這是第一個讀者,搶占 w
        sem_post(&mutex);
      
        // Critical section
        // Reading...
      
        sem_wait(&mutex);
        readcnt--;
        if(readcnt == 0)
            sem_post(&w); // 如果這是最后一個讀者,釋放 w
        sem_post(&mutex);
    }
}

void writer()
{
    while(1)
    {
        sem_wait(&w);
            
        // Critical section
        // Writing...
        
        sem_post(&w);
    }
}

綜合:基于預執行緒化的并發服務器

很好的例子,結合上述多種方式的優點,建議親自寫一遍,代碼參考 CSAPP,不再贅述,

12.6 使用執行緒提高并行性

通用技術:向對等執行緒傳遞一個小整數,作為唯一的執行緒 ID,每個對等執行緒根據執行緒 ID 來決定它應該計算序列的哪一部分,

通常每個核上運行一個執行緒,在一個核上運行運行多個執行緒會有額外的背景關系切換開銷,

多執行緒求和的例子:

執行緒數 1 2 4 8 16
sum_mutex 68.00 432.00 719.00 552.00 599.00
sum_global 7.26 3.64 1.91 1.85 1.84
sum_local 1.06 0.54 0.28 0.29 0.30
// 加鎖操作全域變數
void* sum_mutex(void* vargp)
{
    // 根據 vargp 確定計算范圍
    for(i=start; i<end; i++) {
        sem_wait(&mutex);
        gsum += i;
        sem_post(&mutex);
    }
    return NULL;
}

// 每個執行緒獨立位置存放結果,無需 mutex,直接累加到全域陣列,主執行緒等待所有子執行緒完成
void* sum_global(void* vargp)
{
    long threadId = *((long*) vargp);
    // 根據 vargp 確定計算范圍
    for(i=start; i<end; i++)
        gsum[threadId] += i;
    return NULL;
}

// 先用區域變數累加結果,減少不必要的記憶體參考,最后一次性賦給全域陣列
void* sum_local(void* vargp)
{
    // 根據 vargp 確定計算范圍
    int local_sum = 0;
    for(i=start; i<end; i++) {
        local_sum += i;
    }
    gsum[threadId] = local_sum;
    return NULL;
}

12.7 其他并發問題

執行緒安全

執行緒安全(thread-safe):多個并發執行緒反復呼叫,結果正確

可重入(reentrant):執行緒安全的真子集,不需要同步操作,比不可重入的執行緒安全的函式更高效,

四類不相交的執行緒不安全函式:

不安全類 說明 例子 變為執行緒安全的方法
1 不保護共享變數的函式 - 同步操作保護共享變數;缺點:慢
2 保持跨越多個呼叫的狀態的函式 偽亂數生成器:當前呼叫結果依賴前次呼叫的中間結果 唯一方式是重寫,不再依賴 static 資料,而是依靠呼叫者在引數中傳遞狀態
3 回傳指向靜態變數的指標的函式 ctime、gethostbyname:將結果保存在 static 變數中,然后回傳這個變數的指標 a) 重寫:呼叫者傳遞存放結果的變數地址; b) 如果難以修改,則創建包裝函式,進行加鎖-復制,
4 呼叫執行緒不安全函式的函式 f 呼叫執行緒不安全函式 g 如果 g 是第 2 類,只能重寫 g;如果 g 是 1、3 類,可以加鎖(拷貝)

Linux 中執行緒不安全函式

大多數 Linux 函式都是執行緒安全的,包括定義在 C 庫中的函式(例如 malloc、free、realloc、printf、scanf)

執行緒不安全函式 執行緒不安全類 Linux 執行緒安全版本
rand 2 rand_r
strtok(已棄用) 2 strtok_r
asctime 3 asctime_r
ctime 3 ctime_r
gethostbyaddr(已棄用,推薦 getaddrinfo) 3 gethostbyaddr_r
gethostbyname(已棄用,推薦 getnameinfo) 3 gethostbyname_r
net_ntoa(已棄用,推薦 inet_ntop) 3
localtime 3 localtime_r

死鎖

  • 分析工具:進度圖
  • 一個死鎖的例子:執行緒 A 持有 mutex1,等待 mutex2;執行緒 B 持有 mutex2,等待 mutex1
  • 避免死鎖的最簡單方式——互斥鎖加鎖順序規則:給定所有互斥操作的一個全序,如果每個執行緒都是以一種順序獲得互斥鎖并以相反的順序釋放,那么這個程式就是無死鎖的,

注:現代 C++ 可以一次獲得多個鎖,從根源上避免了死鎖,

12.8 小結

  • 并發:時間上重疊的邏輯流
  • 三種并發機制:行程、I/O 多路復用和執行緒
    • 行程由內核調度,獨立虛擬地址空間,只能顯式 IPC 共享資料
    • 事件驅動程式有自己的并發邏輯流(模型化為狀態機),用 I/O 多路復用來顯式調度這些流
    • 執行緒:內核自動調度,單一行程背景關系
  • 信號量解決共享資料的并發訪問問題,提供互斥訪問,也支持生產者-消費者、讀者-寫者
  • 被執行緒呼叫的函式必須執行緒安全,有四類執行緒不安全的函式
  • 可重入函數比不可重入函式更高效,因為不需要任何同步操作
  • 小心競爭和死鎖

Reference

  • CSAPP 并發編程讀書筆記

  • 現代 C++ 對多執行緒/并發的支持 A Tour of C++(上)

  • 現代 C++ 對多執行緒/并發的支持 A Tour of C++(下)

  • https://www.cnblogs.com/tengzijian/tag/多執行緒/

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/387732.html

標籤:其他

上一篇:上午在改BUG,下午就被通知優化了,給所有做測驗的朋友提個醒

下一篇:動作捕捉助力人-機運動協同性輔助側翻康復輔具設計

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more