多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php框架 > 框架設計 > [CSAPP筆記][第十二章并發編程]

[CSAPP筆記][第十二章并發編程]

來源:程序員人生   發布時間:2016-06-12 08:56:52 閱讀次數:3694次

第102章 并發編程

如果邏輯控制流在時間上是堆疊,那末它們就是并發的(concurrent)。這類常見的現象稱為并發(concurrency)

  • 硬件異常處理程序,進程和Unix信號處理程序都是大家熟習的例子。

我們主要將并發看作是1種操作系統內核用來運行多個利用程序的機制。

  • 但是,并發不單單局限于內核。它也能夠在利用程序中扮演重要的角色。

    • 例如

      • Unix信號處理程序如何允許利用響應異步事件
        • 例如:用戶鍵入ctrl-c
        • 程序訪問虛擬存儲器的1個未定義的區域
    • 其他情況

      • 訪問慢速I/O裝備

        • 當1個利用程序正在等待來自慢速I/O裝備(例如磁盤)的數據到達時,內核會運行其他進程,使CPU保持繁忙。
      • 與人交互

        • 和計算機交互的人要求計算機有同時履行多個任務的能力。
      • 通過推延工作以下降延遲

        • 有時,利用程序能夠通過推延其他操作和并發履行它們,利用并發來下降某些操作的延遲
      • 服務多個網絡客戶端
        • 1個慢速的客戶端可能會致使服務器謝絕為所有客戶端提供服務。
      • 在多核機器上進行并行運算

使用利用級并發的利用程序稱為并發程序(concurrent program).

  • 操作系統提供3種基本的構造并發程序的方法:

    • 進程

      • 每一個邏輯控制流 都是1個進程

        • 由內核來調度和保護。
      • 由于進程有獨立的虛擬地址空間

        • 和其他進程通訊,控制流必須使用某種顯式的進程間通訊(interprocess communication,IPC)進制
    • I/O多路復用(暫時不太懂)
      • 利用程序在1個進程的上下文中顯示地調度它們自己的邏輯流
      • 邏輯流被模型化為狀態機,數據到達文件描寫符后,主程序顯式地從1個狀態轉換到另外一個狀態。
      • 由于程序是1個單獨的進程,所以所有的流都同享同1個地址空間。
    • 線程
      • 線程是運行在1個單1進程上下文中的邏輯流,有內核調度。
        • 進程1樣由內核進行調度。
        • 而像I/O多路復用1流1樣同享1個虛擬地址空間。

12.1 基于進程的的并發編程

1個構造并發服務器的自然方法就是,在父進程中接收客戶端連接要求,然后創建1個新的子進程來為每一個新客戶端提供服務。

  • 服務器正在監聽1個監聽描寫符(描寫符3)上的連接要求
  • 服務器接收客戶端1的連接要求
  • 并返回1個已連接描寫符(描寫符4)。

  • 子進程取得服務器描寫符表的完全拷貝(描寫符3,4)
  • 子進程關閉它的拷貝中的監聽描寫符3
  • 服務器關閉描寫符表中的描寫符4

  • 以后新的客戶端又類似之前兩個步驟。

12.1.1 基于進程的并發服務器

  • Signal(SIGCHLD,sigchld_handler)回收僵死進程。

    • 具體細節見8.5.7
  • 28行,33行 父子進程各自關閉他們不需要的拷貝。

  • 由于文件表項的援用計數,直到父進程關閉它的描寫符,才算結束1次連接

12.1.2 關于進程的優劣

對在父,子進程間同享狀態信息,進程有1個非常清晰的模型

  • 同享文件表,但是不同享用戶地址空間
  • 進程具有獨立的虛擬地址空間即是 優點,也是 缺點

    • 優點:1個進程不可能不謹慎覆蓋另外一個進程的虛擬存儲空間。

      • 消除許多使人迷惑的毛病。
    • 缺點:獨立的地址空間使得進程間同享信息也很困難。

      • 必須使用顯式的IPC(進程間通訊)機制。

      • 常常還比較

        • 進程控制IPC的開消都很大。

12.2 基于I/O多路復用的并發編程(暫時跳過)

假定要編寫1個echo服務器

  • 服務器既能響應客戶端的要求
  • 也能對用戶從標準輸入輸出的交互命令做出反應(如exit).

因此,服務器必須要響應兩個相互獨立的I/O事件

  • 網絡客戶端發起連接
  • 用戶在鍵盤鍵入命令行。

不管先等待那個事件都不是理想的,解決辦法之1是就是使用I/O多路復用技術

  • 基本的思路
    • 使用select函數,要求內核掛起進程,只有1個或多個I/O事件產生后,才將控制返回給利用程序。

12.3 基于線程的并發編程

線程(thread) 就是運行在進程上下文中的邏輯流。

  • 線程由內核調度。
  • 每一個線程都有它自己的線程上下文(thread context).

    • 包括1個唯1的整數線程ID(Thread ID,TID).
    • 棧和棧指針
    • 程序計數器
    • 通用目的寄存器和條件碼
  • 所有運行在該進程里的線程同享該進程的全部虛擬地址空間。

    • 同享 包括代碼,數據,堆,同享庫和打開的文件。

12.3.1 線程履行模型

  • 每一個進程開始生命周期時都是單1線程,這個線程稱為主線程(main thread)

    • 某時刻,主線程創建1個對等線程(peer thread)
      • 當主線程履行1個慢速系統調用,例如readsleep
      • 或被系統的間隔計時器中斷。
      • 控制就會通過上下文切換傳遞到對等線程
      • 對等線程履行1段時間,將控制傳遞回主線程。
  • 在某些方面,線程履行是不同等于進程的。

    • 線程的上下文切換的開消比進程的小很多,快很多
    • 線程不是依照嚴格的父子層次來組織。
      • 和1個進程相干的線程組成1個線程池(pool)
        • 線程池概念的主要影響是
        • 1個線程可以殺死它的任何對等線程,或等待任意對等線程終止。
        • 每一個對等線程都能讀寫相同的同享數據。

12.3.2 Posix 線程

Posix線程 (Pthreads)是在C程序中處理線程的1個標準接口。

  • 在大多數Unix系統可用
  • 允許程序創建,殺死和回收線程,與對等線程安全同享數據,還可以通知對等線程系統狀態的變化。

這是我們第1個線程化的代碼,仔細解析。

  • 線程的代碼和本地數據被封裝在1個線程例程(thread routine)中。

    • 如第2行代碼所示:每一個線程例程都以1個通用指針作為輸入,并返回1個通用指針。
    • 如果想傳遞多個參數給線程例程

      • 你應當將參數放到1個結構中。
      • 并傳遞1個指向該結構的指針
    • 如果想要線程例程返回多個參數。

      • 也能夠返回1個指向結構的指針
  • tid寄存對等線程的線程ID

  • 主線程調用pthread_create函數創建1個新的對等線程(第7行)。

    • 當對pthread_create的調用返回時,主線程和新創建的對等線程同時運行。
  • 通過調用pthread_join,主線程等待對等線程的終止。

  • 對等線程輸出Hello,world

  • 主線程終止。

12.3.3 創建線程

線程通過調用pthread_create函數來創建其他線程。

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

int phread_create(pthread_t *tid,pthread_attr_t *attr,fun *f,void *arg)

                    //若成功則返回0,出錯則為非0

pthread_create函數創建1個新的線程。

  • 帶著1個輸入變量arg,在新線程的上下文中運行線程例程f.
  • 能用attr參數改變新創建線程的默許屬性。

    • 改變這些屬性超過我們的學習范圍。
    • 我們總是用NULL作為attr的參數。
  • pthread_create返回時,參數tid包括新創建線程的ID

    • 通過調用pthread_self函數來取得它自己的線程ID

12.3.4 終止線程

1個線程是以以下方式之1來終止

  • 當頂層的線程例程返回時,線程會隱式地終止
  • 通過調用pthread_exit函數,線程會顯示地終止

    • 如果主線程調用pthread_exit.
      • 等待所有其他對等線程終止,然后終止主線程和其他全部進程。
      • 返回值為thread_return
    • 原型以下

      #include<pthread.h>
      
      void pthread_exit(void *thread_return)
          //成功返回0,出錯返回非0
      
  • 某個對等線程調用Unixexit函數,函數終止進程和所有與該進程有關的線程

  • 對等線程通過以當前線程ID為參數調用pthread_cancle函數來終止當前線程。

    • 原型

      #include<pthread.h>
      
      void pthread_cancle(pthread_t tid);
          //成功返回0,出錯返回非0
      

12.3.5 回收已終止的資源

線程通過調用pthread_join函數等待其他進程終止

#include<pthread.h>

int pthread_join(pthread_t tid,void **thread_return);

            //返回,成功則為0,出錯為非0
  • pthread_join函數會阻塞,知道線程tid終止,將線程返回的(void *)指針賦值給thread_return所指向的位置,然后回收已終止線程占用的存儲器資源。

  • pthread_join不像wait函數1樣等待任意1個線程的結束。

    • 使得用不那末直觀的方式,檢測1個進程的終止。
    • Stevens在書中指出這是1個設計毛病。

12.3.6 分離線程

在任何1個時間點上,線程是可結合的(joinable)或 是分離的(detached)

  • 1個可結合的線程能夠被其他線程收回其資源或殺死。

    • 在被其他線程回收之前,它的存儲器資源是沒有被釋放的。
  • 1個分離的線程是不能被其他線程收回其資源或殺死。

    • 系統自動釋放資源。

pthread_detach函數分離可結合線程tid

#include<pthread.h>

int pthread_detach(pthread_t tid);

            返回:若成功則返回0,若出錯則返回非零。

12.3.7 初始化線程

pthread_once函數允許你初始化與線程例程相干的狀態。

#include<pthread.h>

pthread_once_t once_control = PTHREAD_INIT;

int phread_once(phread_once_t *once_control,void (*init_routine)(void));
  • once_control變量是1個全局或靜態變量,總是被初始化為PTHREAD_ONCE_INIT.
  • 當你第1次用參數once_control調用pthread_once時,它調用init_routine

    • 這是1個沒有輸入參數,也不返回甚么的函數。
  • 第2次,第3次以參數once_control調用pthread_once時,啥事也不產生。

    • 意思時僅僅第1次調用時有效果。
  • 當你需要動態初始化多個線程同享的全局變量時,pthread_once函數是很有用的。

12.3.8 1個基于線程的并發服務器


  • 注意使用malloc動態給1個connfdp,否則可能兩個線程援用同1個connfdp的地址。

    • 這叫做競爭
  • 為在線程例程中避免存儲器泄漏,使用分離線程

  • 還要注意釋放在主線程malloc的變量。

12.4 多線程程序中的同享變量

為了解1個C程序中的1個變量是不是同享,有1些基本的問題要解答

  • 線程的基礎存儲器模型是甚么?
  • 根據這個模型,變量實例是如何映照到存儲器的?
  • 有多少線程援用這些實例?

為了使同享討論具體化,使用下圖的程序作為示例。

示例程序由1個創建兩個對等線程的主線程組成。主線程傳遞1個唯1的ID給每一個對等線程,每一個對等線程利用這個ID輸出1個個性化的信息,和調用該線程例程的總次數。

12.4.1 線程存儲器模型

12.4.2 將變量映照到存儲器

線程化的C程序中的變量根據它們的存儲類型被映照到虛擬存儲器:

  • 全局變量

    • 全局變量是定義在函數以外的變量。
      • 在運行時,虛擬存儲器中的讀/寫區域包括每一個全局變量的1個實例。
      • 任何線程都可以援用。
      • 例如,第5行聲明的ptr
  • 本地自動變量

    • 本地自動變量就是定義在函數內部但是沒有static屬性的變量。
      • 在運行時,每一個線程的包括它自己的所有本地自動變量的實例。
  • 本地靜態變量

    • 本地靜態變量是定義在函數內部有static屬性的變量。
      • 和全局變量1樣,存儲在虛擬存儲器的讀/寫區域
      • 例如:第25行的cnt.

12.4.3 同享變量

我們說1個變量v同享的,當期僅當它的1個實例被1個以上的線程援用。

例如:

  • cnt 是同享的
  • myid 不是同享的
  • 認識到msgs這類本地自動變量也能被同享是很重要的。

12.5 用信號量同步線程

同享變量10分方便,但是他們也引入了同步毛病(synchronization error)的可能性。

斟酌下圖的程序。

到底哪里出錯了呢?這個毛病10分隱晦,必須通過研究計數器循環時的匯編代碼才能看出。

badcnt.c中的兩個對等線程在1個單處理器上并發履行,機器指令以某種順序1個接1個地完成。同1個程序每次運行的順序都可能不同,這些順序中有1些將會產生正確結果,但是其他的不會。這就是同步毛病

關鍵點: 1般而言,你沒有辦法預測操作系統是不是將為你的線程選擇1個正確的順序

  • 下圖,就是cnt正確的順序和毛病的順序(正確結果cnt=2,毛病結果cnt=1)

我們可以借助于1種叫做進度圖(progress graph)的方法來闡明這些正確和不正確的指令順序的概念。將在接下來介紹。

12.5.1 進度圖

進度圖(process graph)n個并發進程的履行模型化為1條n維笛卡爾空間的軌跡線

  • 每條軸k對應于k的進度。

  • 每一個點(I1,I2,I3,I4...,In)代表線程k(k=1,...,n)已完成到了Ik這條指令的狀態。

  • 圖的原點對應于沒有任何線程完成這1條指令的初始狀態


進度圖將指令履行模型化為從1個狀態到另外一個狀態的轉換(transition)

  • 轉換指從1點到相鄰1點的有向邊。
    • 合法的轉換是向各個軸的正半軸走。

臨界區

對線程i,操作同享變量cnt內容的指令(Li,Ui,Si)構成了1個(關于同享變量cnt的)臨界區(critical section)。(必須確保指令要這樣履行)

  • 這個臨界區不應當和其他線程的臨界區交替履行。(這1段的指令不能交叉)。

  • 我們要確保每一個線程在履行它的臨界區中的指令時,具有對同享變量的互斥的訪問(mutually exclusive access)

    • 通常這類現象叫做互斥(mutual exclusion)

不安全區

在進程圖中,兩個臨界區的交集情勢稱為不安全區(unsafe region)

  • 不安全區邊沿的不算不安全區的1部份。

安全軌跡線,不安全軌跡線

  • 繞過不安全區的軌跡線叫做安全軌跡線
    • 能正確更新計數器
  • 接觸到不安全的軌跡線叫做不安全軌跡線

我們必須以某種方式同步線程,使它們總是有1條安全軌跡線

  • 1個經典的方法,就是基于信號量的思想。

12.5.2 信號量

Edsger Dijksta,并發編程領域的先鋒任務,提出了1種經典的解決同步不同履行線程問題的方法

這類方法是基于1種叫做信號量(semaphore)的特殊類型變量。

  • 信號量s是具有非負整數值的全局變量。

  • 只能由兩種特殊的操作來處理,這兩種操作稱為PV

    • P(s),Proberen,測試

      • 如果s是非零的,那末P操作s減1,并且立即返回。
      • 如果s為零,那末就掛起這個線程,直到s變成非零。
        • 而1個V操作會重啟這個線程。
        • 在重啟以后,P操作s減1,并將控制返回給調用者。
    • V(s),Verhogen,增加

      • V操作s加1.
      • 如果有任何線程阻塞在P操作等待s變成非零。
        • 那末V操作隨機會重啟這些線程中的1個。
        • 然后將s減去1,完成它的P操作
    • 重點P操作V操作都是不可分割的,也就是本身確保了是1個帶有安全軌跡的操作。(所以又叫原語)

      • 對照,上文中的cnt++的操作。
      • 例如,加1這個操作中,加載,加1,存儲信號量進程是不可分割的。

PV的定義確保了1個正在運行的程序絕不可能進入這樣1種狀態,也就是不可能有負值。
這個屬性叫做信號量不變性(semaphore invariant),為控制并發程序的軌跡線提供了強有力的工具。

12.5.3 使用信號量來實現互斥

信號量提供了1種很方便的方法來確保對同享變量的互斥訪問。

基本的思想是

  • 將每一個同享變量(或1組相干的同享變量) 與1個信號量s(初始為)`聯系起來。
  • 然后用P(s)V(s)操作相應的臨界區包圍起來。

以這類方式保護同享變量的信號量叫做2元信號量(binary semaphore)

  • 由于它的值總是0或1。

以提供互斥為目的的2元信號量常常也稱為互斥鎖(mutex)

  • 在1個互斥鎖上履行P操作叫做互斥鎖加鎖
  • 在1個互斥鎖上履行V操作叫做互斥鎖解鎖
  • 對1個互斥鎖加了鎖還沒有解鎖的線程稱為占用這個互斥鎖

1個被用作1組可用資源的計數器的信號量稱為計數信號量

關鍵思想:

  • P操作V操作的結合創建了1組狀態,叫做制止區(forbidden regin),其中s<0
    • 由于信號量的不變形,不可能有軌跡線進入這個區域
    • 而且制止區包括了不安全區的任何部份。
      • 使得,每條可行的軌跡線都是安全的。

代碼上的實現

正確切現上文中的cnt的線程同步。

  • 第1步:聲明1個信號量 mutex

    volatile int cnt = 0 ; 
    sem_t mutex;
    
  • 第2步:主線程中初始化

    Sem_init(&mutex,0,1);
    
  • 第3步,在線程例程中對同享變量cnt的更新包圍PV操作,從而保護了它們。

    for( i = 0 ;i < niters ;i++) {
        P(&mutex);
        cnt++;
        V(&mutex);
    }
    

12.5.4 利用信號量來調度同享資源

除提供互斥外,信號量的另外一個重要作用是調度對同享資源的訪問

  • 在這類場景中,1個線程用信號量操作來通知另外一個線程,程序狀態中的某個條件為真了。
  • 兩個經典而有用的例子。

    • 生產者 - 消費者 問題
    • 讀者 - 寫者 問題

1.生產者和消費者

圖給出了生產者消費者問題

  • 生產者線程反復地生成新的項目,并把它們插入到緩沖區中。
  • 消費者線程不斷地從緩沖區取出這些項目,然后消費使用它們。
  • 也可能有多個的變種。

由于插入和取出項目都觸及更新同享變量

  • 所以我們必須保證對緩沖區的訪問是互斥的
  • 還需要調度對緩沖區的訪問。
    • 如果緩沖區是滿的,那末生產者必須等待直到有1個槽位變成可用。
    • 如果緩沖區是空的,那末消費者必須等待知道有1個項目變成可用。

我們將開發1個簡單的包,叫做SBUF,用來構造生產者-消費者程序。

SBUF操作類型為sbuf_t的有限緩沖區。

  • 項目寄存在1個動態分配的n項整數數組(buf)中。
  • frontrear索引值記錄該隊列的第1項和最后1項。
  • 3個信號量同步對緩沖區的訪問。
    • mutex信號量提供互斥的緩沖區訪問
    • slotsitems信號量分別記錄空槽位和可用項目的數量。

以下給出SBUF函數的實現:

  • sbuf_init函數進行初始。
    • 為緩沖辨別配堆存儲器
    • 設置frontrear表示1個空的緩沖區。
    • 并為3個信號量賦初值。

  • sbuf_deinit函數是當利用程序使用完緩沖區時,釋放緩沖區存儲。
  • sbuf_insert

    • 等待1個可用的槽位
    • 對互斥鎖加鎖,添加項目,對互斥鎖解鎖
    • 然后宣布有1個新項目可用。
  • sbuf_remove

    • 等待1個可用的項目
    • 對互斥鎖加鎖,取出項目,對互斥鎖解鎖
    • 然后宣布有1個新槽位可用。

2.讀者-寫者問題

讀者-寫著問題是互斥問題的1個概括。

  • 1組并發的線程要訪問同1個數據對象。

    • 修改對象的線程叫做寫者
    • 只讀對象的線程叫做讀者
  • 寫者必須具有對對象的獨占訪問。

  • 讀者可以和無窮多個其他讀者同享對象。
  • 1般來講有沒有數個并發的讀者和寫者。

讀者-寫者問題有幾個變種,都是基于讀者和寫者的優先級

  • 第1類讀者-寫者問題

    • 讀者優先,要求不要讓讀者等待,除非已把1個使用權限賦予了1個寫者
    • 換句話說,讀者不會由于有1個寫者在等待而等待。
  • 第2類讀者-寫者問題(?)

    • 寫者優先,要求1但1個寫者準備好可以寫,它就會盡量地完成它的寫操作。
    • 同第1類不同,在1個寫者到達后的讀者必須等待,即便這個寫者也是在等待。

給出第1類讀者-寫者問題答案。

  • 這個的優先級很弱,由于1個離開臨界區的寫者可能重啟1個在等待的寫者(隨機重啟)
    • 很有可能1群寫者使得1個讀者饑餓

  • 信號量w控制對訪問同享對象的臨街區的訪問。

    • 讀者

      • w只對第1個讀者上鎖
      • w對最后1個走的讀者解鎖
    • 寫者

      • 寫者只要進入臨界區就對w上鎖
      • 寫者只要離開臨界區就對w解鎖
  • 信號量mutex保護對同享變量readcnt的訪問。
    • readcnt統計當前臨界區的讀者數量。

所有讀者-寫者答案都有可能致使饑餓

  • 饑餓就是1個線程無窮期地阻塞,沒法進展。

12.5.5 基于預線程化的并發服務器

為每一個新的客戶端創建新的線程,有很多的代價。

1個基于預線程化服務器利用生產者-消費者模型構造1個更高效力的方式。

  • 生產者: 主線程
  • 消費者: 對等線程

12.6 使用線程提高并行性(暫略)

主要用于多核CPU的算法。

比如:利用并行來完成n路遞歸

12.7 其他并提問題

互斥生產者-消費者同步的技術,只是并提問題的冰山1角。

同步問題從根本來講是很難的問題。

這章我們以線程為例討論。

  • 但是要知道同步問題在任何并發流操作同享資源時都會出現。
    • 比如之前學信號時,回收進程時的競爭

12.7.1 線程安全

1個函數被稱為線程安全的(thread-safe),當且僅當被多個并發線程反復地調用時,它會1直產生正確的結果。否則就是線程不安全的(thread-unsafe)

我們能夠定義出4個(不相交)線程不安全函數類:

  • 第 1 類 : 不保護同享變量的函數。
    • 解決方案,利用P,V這樣的同步操作來保護同享的變量
  • 第 2 類 : 保持逾越多個調用狀態的函數

    • 1個偽隨機數生成器是這類線程不安全的例子。

    • 由于產生的結果依賴于上1個next的值。

      • 在單線程中,用同1個seed不管運行多少次,都是一樣的結果。
      • 多線程中,這類情況就不會出現了,所以是線程不安全
    • 解決方案: 重寫

      • 使得它不能依賴static,而是依托調用者在參數中傳遞狀態信息。
      • 缺點: 需要在曾成千上百個不同的調用位置,修改。10分麻煩。
  • 第 3 類 :返回指向靜態變量的指針的函數( 有點類似第1類 )

    • 危害:我們在并發線程中調用這些函數,可能產生災害。
      • 由于1個正在被1個線程使用的變量,可能偷偷被另外一個線程悄悄覆蓋。
    • 解放方案

      • 重寫函數:讓調用者傳遞寄存的結果的指針
      • 加鎖-拷貝技術:

        • 在1個調用位置,互斥鎖加鎖。
        • 調用線程不安全函數,將函數返回的結構拷貝到1個私有的存儲器。
        • 然后互斥鎖,解鎖。
      • 用上面的原理寫1個線程不安全函數的包裝函數來實現線程安全。

        • 以ctime為例子

  • 第 4 類 : 調用線程不安全函數的函數

    • 如果函數f調用線程不安全函數g。那末f可能不安全。
      • 如果g是第2類,那末f1定不安全,也沒有辦法去修正,只能改變g.
      • 如果g是第1,3類,可以用加鎖-拷貝技術來解決。

12.7.2 可重入性

有1類重要的線程安全函數,叫做可重入函數(reentrant function)

  • 其特點在于它們有這樣1種屬性。

    • 當它們被多個線程調用時,不會援用任何同享數據
  • 被分為兩類

    • 顯示可重入
      • 參數都是值傳遞
      • 變量都是本地自動棧變量
    • 隱式可重入

      • 參數可以有指針

        • 但是不允許調用者傳入指向同享數據的指針。
      • 是不是可重入,同時取決于調用者,和被調用者

  • 可重入函數比較高效是由于不需要同步操作。

  • 認識到可重入性有時即是調用者也是被調用者的屬性。

    • 其實不是被調用者的單獨屬性。

12.7.3 在線程化的程序中使用已存在的庫函數

大多數Unix函數,包括大部份定義在標準C庫的函數(malloc,free,realloc,printfscanf)都是線程安全的。

部份線程不安全

  • asctime,ctime,localtime函數是在不同時間和數據格式相互來回轉換時常常使用的函數。

  • gethostbyname,gethostbyaddr,inet_ntoa函數是常常用的網絡編程函數。

  • strtok函數是1個過時了的同來分析字符串的函數。


Unix系統提供大多數線程不安全函數的可重入版本。

  • 可重入的版本總是以 _r后綴結尾。
  • 例,gethostbyname_r

12.7.4 競爭

當1個程序的正確性依賴于1個線程要在另外一個線程到達y點之前到達它的控制流中的x點,就會產生競爭

  • 通常,競爭產生的理由是由于程序員假定某種特殊的軌跡線穿過履行狀態空間。

例子:

程序10分簡單。

主線程創建了4個對等線程,并傳遞1個指向循環變量i的指針作為線程的ID。并輸出。

  • 1般而言,循環變量i1定是4個不同的。所以會想固然覺得會輸出4個不同的ID

  • 但是從結果來看,明顯是毛病的,有兩個3,為何?
    • 由于我們想固然的覺得對等線程myid賦值結束后,i才會自增。
    • 競爭來源于 主線程中i++,和對等線程myid=*((int *)vargp)競爭

解決方案:用1個臨時地址保存i

12.7.5 死鎖

信號量引入了1種潛伏的使人討厭的運行時毛病,叫做死鎖 (deadlock)。

  • 指的是1組線程被阻塞,等待1個永久不為真的條件。

進度圖對理解死鎖是1個無價的工具。

  • 死鎖的區域d是1個只能進不能出的區域。

    • 位置是合法的,其實不是制止區,能進去。
    • 但是會發現不管向上,還是右,都只剩下制止區了。
  • 如果制止區不堆疊,1定不會產生死鎖

    • 否則,可能產生死鎖
  • 死鎖是1個相當困難的問題,由于它總是不可預測的。

    • 榮幸的話,會繞開死鎖區域。
    • 毛病還不會重復,軌跡不同。

特殊解

使用2元信號量來實現互斥,可以利用1下有效的規則。

互斥鎖加鎖順序規則:如果對程序中每對互斥鎖(s,t),每一個占用st的線程都依照相同的順序對它們加鎖,那末這個程序就是無死鎖的。


GGGGGGGGGGG,暫時告1段落了!!!!!!!!!!!!!!ddd!!

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 国产欧美另类性视频 | 国产v片在线观看 | 国内一区二区三区精品视频 | 精品在线第一页 | 国产l精品国产亚洲区久久 国产mv在线观看 | 亚洲黄色影片 | 美国一级特级毛片片aa视频 | 欧美精品亚洲精品日韩 | 一级日本特黄毛片视频 | 爽好舒服快奶水视频 | 国产xxxxfreexxxxxvideo| 精品视频一区二区三区在线观看 | 五月天伊人 | 国产日韩欧美精品一区二区三区 | 色视频2 | 欧美激情在线视频播放 | 日韩成人免费视频播放 | 国产在线视频一区二区三区 | h网站免费观看 | 亚洲精品国自产拍在线观看 | 日韩综合久久 | 激情小说亚洲图片 | 欧美国产日韩久久久 | 久久久久久久久久久96av | 日韩精品欧美精品中文精品 | 国产精品天天看 | 国产精品日韩一区二区三区 | 欧洲自拍偷拍 | 亚洲五月婷 | 好好的曰www视频在线观看 | 日韩精品久久不卡中文字幕 | 福利视频一区二区 | 中文字幕一区二区三区有限公司 | 最新亚洲人成网站在线影院 | 国产成人久久精品二区三区 | 91精品国产人成网站 | 亚洲精品色一区二区三区 | 免费观看欧美性一级 | 国产做人爱三级视频在线 | 在线精品亚洲欧洲第一页 | 国产成人鲁鲁免费视频a |