1. 程式人生 > >APUE 學習筆記——執行緒控制

APUE 學習筆記——執行緒控制

第12章 執行緒控制

1.  執行緒屬性

執行緒屬性結構體:pthread_arrr_t

int pthread_attr_init(pthread_attr_t *attr);  //初始化執行緒屬性

int pthread_attr_destroy(pthread_attr_t*attr); //釋放屬性物件記憶體

執行緒屬性主要有:

(1)執行緒的分離狀態屬性detachstate,

(2)執行緒棧末尾的警戒緩衝區大小guardsize,

(3)執行緒棧的最低地址statckaddr,

(4)執行緒棧的大小stacksize。

其中常用的是分離狀態屬性:

int pthread_attr_getdetachstate

(pthread_attr_t*attr, int *detachstate);//獲取當前的分離狀態屬性

int pthread_attr_setdetachstate(pthread_attr_t*attr, intdetachstate); //設定分離狀態屬性

detatchstate取值:

(1)PTHREAD_CREATE_DETACHED分離狀態啟動,

(2)PTHREAD_CREATE_JOINABLE正常啟動,應用程式可以獲取執行緒的終止狀態。

2.  同步屬性

1)  互斥量屬性

程序共享屬性和型別屬性兩種

         i.             程序共享屬性

PTHREAD_PROCESS_PRIVATE,預設,程序間不共享,程序私有。用於程序中的執行緒同步。

PTHREAD_PROCESS_SHARED,程序間共享,用於程序的同步。

int pthread_mutexattr_init(pthread_mutexattr_t*attr);  //初始化

int pthread_mutexattr_destroy(pthread_mutexattr_t*attr); //回收

int pthread_mutexattr_getpshared(constpthread_mutexattr_t *restrictattr,int *restrictpshared);  //查詢程序共享屬性

int pthread_mutexattr_setpshared(pthread_mutexattr_t*attr

,intpshared); //設定程序共享屬性

       ii.             型別屬性

int pthread_mutexattr_gettype(constpthread_mutexattr_t *restrictattr,int*restricttype); //查詢型別屬性

int pthread_mutexattr_settype(pthread_mutexattr_t*attr, inttype); //設定型別屬性

互斥量型別

用途

沒有解鎖時再次加鎖

不佔用時解鎖

在已解鎖時解鎖

PTHREAD_MUTEX_NORMAL

標準型別,不做任何檢查

死鎖

未定義

未定義

PTHREAD_MUTEX_ERRORCHECK

程序錯誤檢查

返回錯誤

返回錯誤

返回錯誤

PTHREAD_MUTEX_RECURSIVE

避免死鎖

允許

返回錯誤

返回錯誤

PTHREAD_MUTEX_DEFFAULT

請求預設語義

未定義

未定義

PTHREAD_MUTEX_RECURSIVE型別:遞迴鎖,允許在同一個執行緒在互斥量解鎖之前對該互斥量程序多次加鎖,解鎖次數和加鎖次數不同時不會釋放鎖。

2)  讀寫鎖屬性

只有一個:程序共享屬性,與互斥量類似。

int pthread_rwlockattr_init(pthread_rwlockattr_t*attr);

int pthread_rwlockattr_destroy(pthread_rwlockattr_t*attr);

int pthread_rwlockattr_getpshared(constpthread_rwlockattr_t *restrict attr, int *restrict pshared);

int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*attr,int pshared);

3)  條件變數屬性

與讀寫鎖屬性類似。

3.  執行緒私有資料(執行緒區域性資料)

多執行緒程式設計時,遇到最多的就是多執行緒讀寫同一變數的問題,由於執行緒中的區域性變數只能在函式體內使用,不能跨函式使用,而執行緒的全域性變數又被程序內的所有執行緒所共享,因此存線上程資料安全的需求。大多情況下遇到這類問題都是通過鎖機制來處理,但這對程式的效能帶來了很大的影響。

在一個執行緒內部的各個函式呼叫都能訪問,但其它執行緒不能訪問的變數,稱之為執行緒特有資料(TSD: Thread-SpecificData)或者執行緒區域性儲存(TLS:Thread-Local Storage)。這一型別的資料,在程式中每個執行緒都會分別維護一份變數的副本,並且長期存在於該執行緒中,對此類變數的操作不影響其他執行緒。

其實現機制是:

程序內分配一個全域性陣列,陣列中的每個元素是一個結構體,其包含兩個變數:標記該鍵是否在用的標誌變數、執行緒區域性變數的解構函式的函式指標。當呼叫pthread_key_create()建立一個鍵時,得到的實際上是該陣列的一個索引值。系統根據“在用”標誌查詢一個未用的元素,將“在用”標誌置為used,然後存入該變數的解構函式指標,最後返回該陣列的索引值。

 

另外,在每個執行緒內,執行緒還分別維護一個數組,存放每個執行緒的執行緒特有資料塊的地址。pthreadAPI為每個函式都維護指向執行緒區域性資料塊的指標陣列,其中每個陣列元素都與程序內全域性pthread_keys中元素一一對應。當呼叫pthread_setspcific()函式時,將執行緒特有資料塊的指標放入與該Key相對應的陣列元素位置。這樣,也就將執行緒私有資料繫結(關聯)到了該鍵上。呼叫pthread_getspecific() 即可取出所儲存的資料。


表面上看起來這是一個全域性變數(Key),所有執行緒都可以使用它,而它的值在每一個執行緒中又是單獨儲存的,都是各個執行緒私有的。

下面說一下執行緒私有資料的具體用法。

1)首先建立一個型別為pthread_key_t 型別的變數key。

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

第一個引數就是上面宣告的pthread_key_t 變數,

第二個引數是一個清理函式,用來線上程釋放該執行緒儲存的時候被呼叫。該函式指標可以設成 NULL,這樣系統將呼叫預設的清理函式。

2)當執行緒中需要儲存特殊值的時候,呼叫pthread_setspcific() 關聯私有資料。

int pthread_setspecific(pthread_key_tkey, const void *value);

第一個為前面宣告的pthread_key_t 變數key,

第二個為void* 變數,指向執行緒私有資料塊,可以儲存任何型別的值。

3)當需要使用私有資料時,呼叫 pthread_getspecific() 。

void *pthread_getspecific(pthread_key_tkey);

傳入引數為鍵,返回void* 型別指標,指向要使用的執行緒私有資料。因此若要使用該資料,還需要一個間接訪問操作。

4)呼叫pthread_key_delete函式取消鍵與私有資料的關聯。

int pthread_key_delete(pthread_key_tkey);

鍵所佔用的記憶體將被釋放。注意,與該鍵關聯的執行緒私有資料所佔用的記憶體並不被釋放。因此,執行緒資料的釋放必須在釋放鍵之前完成。

寫了一個小例子,定義了一個程序全域性變數,即鍵,建立了兩個執行緒,每個執行緒內各定義了一個執行緒私有資料tsd,線上程內可視為全域性變數,可被執行緒內所有函式使用,但線上程之間是區域性變數,是為執行緒所私有的。

#include"apue.h"

#include<pthread.h>

pthread_key_tmykey;//定義一個鍵,執行緒全域性可見

voidprint_tsd(void * arg)

{

int *tsd_p=(int*)pthread_getspecific(mykey);

printf("%sprint_tsd : %d\n",(char*)arg,*tsd_p);//列印執行緒區域性變數tsd的值

}

void* tid2_func(void * arg)

{

        printf("thread2 starting in %s ,tid= %d\n",(char*)arg,(int)pthread_self());

        int tsd=1;//執行緒2私有資料

        pthread_setspecific(mykey,&tsd);

        printf("tid2 tsd's arr is%ld\n",(long int)pthread_getspecific(mykey));

        print_tsd("thread2");

}

void* tid1_func(void * arg)

{

printf("thread1starting in %s , tid= %d\n",(char*)arg,(int)pthread_self());

int tsd=0;//執行緒1私有資料

pthread_setspecific(mykey,&tsd);//與鍵關聯

printf("tid1tsd's arr is %ld\n",(long int)pthread_getspecific(mykey));//列印區域性變數地址

print_tsd("thread1");//在另一個函式中列印執行緒區域性變數的值

}

voidmydestr(void* arg)

{

printf("destroyTSD memory\n");

}

intmain(void)

{

pthread_ttid1,tid2;

pthread_key_create(&mykey,mydestr);//建立鍵

pthread_create(&tid1,NULL,tid1_func,"tid1_func");

sleep(3);

pthread_create(&tid2,NULL,tid2_func,"tid2_func");

sleep(3);

pthread_join(tid1,NULL);

pthread_join(tid2,NULL);

pthread_key_delete(mykey);

exit(0);

}

 

4.  執行緒和IO

pread和pwrite函式

偏移量的設定和資料操作為原子操作,可以解決併發執行緒對同一檔案的讀寫操作問題。

5.  執行緒和訊號

訊號的處理時程序中所有執行緒共享的,當執行緒修改了與某個訊號相關的處理行為後,所有執行緒必須共享這個處理行為的改變。

多執行緒中的訊號機制,與程序的訊號機制有著根本的區別:

在程序環境中,對訊號的處理是:先註冊訊號處理函式,當訊號非同步發生時,呼叫處理函式來處理訊號。它完全是非同步的(我們完全不知道訊號會在程序的那個執行點到來!)。

在多執行緒中處理訊號的原則卻完全不同,它的基本原則是:將對訊號的非同步處理,轉換成同步處理,也就是說用一個執行緒專門的來“同步等待”訊號的到來,而其它的執行緒可以完全不被該訊號中斷/打斷(interrupt)。這樣就在相當程度上簡化了在多執行緒環境中對訊號的處理。而且可以保證其它的執行緒不受訊號的影響。這樣我們對訊號就可以完全預測,因為它不再是非同步的,而是同步的(我們完全知道訊號會在哪個執行緒中的哪個執行點到來而被處理!)。

每個執行緒都可以擁有自己的訊號遮蔽集,可以使用pthread_sigmask函式設定該執行緒的遮蔽訊號集。為防止訊號中斷執行緒,可以把訊號加到每個執行緒的訊號遮蔽字中,然後安排一個專用的執行緒作訊號處理。實現方式是:利用執行緒訊號遮蔽集的繼承關係(在主執行緒中使用pthread_sigmask進行設定後,主執行緒創建出來的執行緒將繼承主執行緒的掩碼)。

int pthread_sigmask (int how,const sigset_t *set,sigset_t *oset);//設定執行緒遮蔽字

成功返回0,出錯返回錯誤編號。

訊號處理執行緒可以呼叫sigwait等待指定訊號發生:

int sigwait(const sigset_t *set, int *sig);

成功返回0,出錯返回錯誤編號。

set指定執行緒等待的訊號集,sig返回發生的訊號值。

執行緒在呼叫sigwait之前,必須阻塞它正在等待的訊號,sigwait呼叫後會自動取消其阻塞狀態,然後執行緒掛起直到等待的訊號被遞送,然後函式在返回之前,重新恢復執行緒訊號遮蔽字。而如果呼叫sigwait之前沒有阻塞要等待的訊號,則有可能在sigwait呼叫之前的時間視窗內到來訊號。

在多執行緒程式中,一個執行緒可以使用pthread_kill對同一個程序中指定的執行緒(包括自己)傳送訊號。注意在多執行緒中一般不使用kill函式傳送訊號,因為kill是對程序傳送訊號,結果是:正在執行的執行緒會處理該訊號,如果該執行緒沒有

 註冊訊號處理函式,那麼會導致整個程序退出。

int pthread_kill(pthread_t thread, intsig);

例:執行緒訊號處理程式框架

#include“apue.h”

#include<pthread.h>

sigset_tmask;

void*sig_handler(void *arg)

{

       int sig;

       for( ; ; )

       {

       if(sigwait(&mask, &sig) != 0)

              err_sys(“sigwait error\n”);

       switch(sig)

       {

              case SIGINT:

              {

       /* SIGINT訊號處理語句 */

       printf(“SIGINT\n”);

       break;

}

caseSIGUSR1:

              {

       /* SIGUSR1訊號處理語句 */

       printf(“SIGUSR1\n”);

       break;

}

caseSIGUSR2:

              {

       /* SIGUSR2訊號處理語句 */

       printf(“SIGUSR2\n”);

       break;

}

default:

{

       /* 其他語句 */

       printf(“unexpected signal: %d\n”,sig);

       break;

}

}

}

}

intmain(void)

{

       sigset_t oldmask;

       pthread_t tid;

       sigemptyset(&mask);

       sigaddset(&mask,SIGINT);

       sigaddset(&mask,SIGUSR1);

       sigaddset(&mask,SIGUSR2);

       if(pthread_sigmask(SIG_BLOCK, &mask,&oldmask)!=0)//設定執行緒訊號遮蔽字

              err_sys(“pthread_sigmask error\n”);

       if(pthread_create(&tid, NULL,sig_handler,0)!=0)//建立訊號處理專用執行緒

              err_sys(“pthread_create error\n”);

       /* 其他程式碼:主執行緒主要執行語句 */

       sleep(30);

       if(sigprocmask(SIG_SETMASK, &oldmask,NULL)<0)

              err_sys(“sigprocmask error\n”);

       exit(0);

}

相關推薦

APUE 學習筆記——執行控制

第12章 執行緒控制 1.  執行緒屬性 執行緒屬性結構體:pthread_arrr_t int pthread_attr_init(pthread_attr_t *attr);  //初始化執行緒屬性 int pthread_attr_destroy(pthread_a

Linux 學習筆記執行間通訊的概念和執行控制函式

1 執行緒間通訊 執行緒間無需特別的手段進行通訊,由於執行緒間能夠共享資料結構,也就是一個全域性變數能夠被兩個執行緒同一時候使用。只是要注意的是執行緒間須要做好同步,一般用mutex。執行緒間的通訊目的主要是用於執行緒同步,所以執行緒沒有像程序通訊中的用於資料交

Linux 學習筆記執行同步之互斥量與條件變數

執行緒同步(同步的意思是協同步調) 執行緒同步機制包括互斥,讀寫鎖以及條件變數等 3.2.1 互斥量(互斥鎖) **互斥量本質是一把鎖,在訪問公共資源前對互斥量設定(加鎖),確保同一時間只有一個執行緒訪問資料,在訪問完成後再釋放(解鎖)互斥量。**在互斥量加鎖之

Linux 學習筆記執行同步之讀寫鎖、自旋鎖、屏障

3.2.1 讀寫鎖 讀寫鎖和互斥體類似,不過讀寫鎖有更高的並行性,互斥體要麼是鎖住狀態,要麼是不加鎖狀態,而且一次只有一個執行緒可以對其加鎖。而讀寫鎖可以有3個狀態,讀模式下鎖住狀態,寫模式下鎖住狀態,不加鎖狀態。一次只有一個執行緒可以佔有寫模式的讀寫鎖,但是多

TBSchedule原始碼學習筆記-執行組任務排程

根據上文的啟動過程,找到了執行緒組的實現。com.taobao.pamirs.schedule.taskmanager.TBScheduleManager /** * 1、任務排程分配器的目標: 讓所有的任務不重複,不遺漏的被快速處理。 * 2、

[APUE chapter 12] 執行控制

作者:isshe 日期:2016.10.30 郵箱:[email protected] github: https://github.com/isshe 1.前言 2. 相關概念 執行緒分離: 就是說與建立執行

python學習筆記 ---執行、程序、協程、佇列、python-memcache、python-redis

一、執行緒 Threading用於提供執行緒相關的操作,執行緒是應用程式中工作的最小單元。 #!/usr/bin/env python # -*- coding:utf-8 -*- import threading import time def show(arg): time.

C++11學習筆記-----執行庫std::thread

在以前,要想在C++程式中使用執行緒,需要呼叫作業系統提供的執行緒庫,比如linux下的<pthread.h>。但畢竟是底層的C函式庫,沒有什麼抽象封裝可言,僅僅透露著一種簡單,暴力美 C++11在語言級別上提供了執行緒的支援,不考慮效能的情況下可

執行程式設計學習筆記——執行同步(三)

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; //引入執行緒 using System.Diagnostics; namesp

Python學習筆記--執行

如果你想啟動python的多執行緒對一個數據進行累加的操作,如果在py2上你多執行幾次會發現少加了。原因是如果某個執行緒拿到公共資料477時,該執行緒就會申請一個gil lock,python直譯器

Java學習筆記 執行池使用及詳解

有點笨,參考了好幾篇大佬們寫的文章才整理出來的筆記.... 字面意思上解釋,執行緒池就是裝有執行緒的池,我們可以把要執行的多執行緒交給執行緒池來處理,和連線池的概念一樣,通過維護一定數量的執行緒池來達到多個執行緒的複用。 好處 多執行緒產生的問題 一般我們使用到多執行緒的程式設計的時候,需要通過new Thr

RxJava2.0學習筆記(簡介,執行控制,常見操作符)

文章轉載自:大神的簡書 要在android中使用RxJava2,先新增Gradle配置: compile 'io.reactivex.rxjava2:rxjava:2.0.1' compile 'io.reactivex.rxjava2:rx

Java學習|多執行學習筆記

什麼是執行緒?     可以理解為程序中獨立執行的字任務。   使用多執行緒:     1.繼承Thread類:從原始碼可以看到,Thread累實現了Runnable介面。     &n

Java筆記-多執行執行控制

執行緒控制 我們已經知道了執行緒的排程,接下來我們就可以使用如下方法物件執行緒進行控制。 1.執行緒休眠 public static void sleep(long millis):讓當前執行緒處於暫停狀態,millis引數毫秒值,即暫停時間。 程式

Linux多執行程式設計學習(1)--執行的概念和執行控制

Linux多執行緒程式設計學習總結 一.執行緒的概念 1.什麼是執行緒? 2.程序和執行緒的區別 3.程序和執行緒的關係 4.執行緒的優缺點 4.1 優點 4.2 缺點 5.執行

java學習執行筆記

學習,享受他的快樂,才不會成為一種苦惱拖累。 隨手寫寫,記錄自己的點滴進步。才會變得更有動力。 今天學習了一下java的執行緒。看到這個名字很隨意,自然的就想到了程序。因為在windows中程序很容易見到。每當某個程式沒有響應後,我們都會很自然的開啟資源管理器,結束任務,結

Robot Operating System (ROS)學習筆記4---語音控制

sla 語音 出現 tput http 學習 process 輸入 ubun 搭建環境:XMWare Ubuntu14.04 ROS(indigo) 轉載自古月居 轉載連接:http://www.guyuehome.com/260 一、語音識別包 1、安裝

StackExchange.Redis學習筆記(四) 事務控制和Batch批量操作

成了 pan arp 展示 關於 public 連續 因此 用戶 Redis事物 Redis命令實現事務 Redis的事物包含在multi和exec(執行)或者discard(回滾)命令中 和sql事務不同的是,Redis調用Exec只是將所有的命令變成一個單元一起執行,期

七LWIP學習筆記之傳輸控制協議(TCP)

輸入 post wait syn 快速重傳 擁塞 斷開連接 其他 time 一、協議簡介 1、TCP的必要性 2、TCP的特性 3、連接的定義 4、數據流編號 5、滑動窗口 二、TCP報文 1、報文格式 2、TCP選項 3、緊急數據 4、強迫數據交互 5、報文首部數據結構

Windbg除錯----多執行控制除錯

在除錯程式的時候,可能經常會有這樣的需求,讓一個執行緒在特定的時候才讓其開始執行或者暫停執行。比如複雜的多執行緒導致死鎖的問題,又或者多執行緒中的Race Condition 導致程式執行異常等。 很多時候,我們可以藉助編寫除錯程式碼來達到多執行緒的除錯,可是有些情況下除錯的執行粒度是指