Linux下多線程編程詳解

簡介

  • 線程創建
  • 線程屬性設置
  • 線程參數傳遞
  • 線程優先級
  • 線程的數據處理
  • 線程的分離狀態
  • 互斥鎖
  • 信號量

一 線程創建

廢話不多說,先上一個線程版的helloworld。

#include <iostream>
using namespace std;

void *run(void *ptr){
        for(int i=0; i<3; i++) {
          sleep(1);
                cout<<"hello world "<<i<<endl;
        }
        return 0;
}
int main(){
        pthread_t id;
        int ret=0;
        ret=pthread_create(&id,NULL,run,NULL);
        if(ret) {
                cout<<"create thread failed "<<endl;
                return 0;
        }
        pthread_join(id,NULL);
        return 0;
}

運行后

hello world 0
hello world 1
hello world 2

上面的代碼很簡單,就是啟動一個線程,然后先線程里循環打印字段字符串。我們就以這個最簡單的例子來開口。

1.1 pthread_create

創建一個線程,函數的聲明:

int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr,
                   void* (*start_routine)(void*), void* arg)
  • thread_out 創建線程后的標識符,下面會介紹。
  • attr 設置線程屬性。傳NULL為默認屬性(對大多數程序來說,使用默認屬性就夠了),當然具體使用下面也會做介紹。
  • start_routine 線程運行函數的起始地址(簡單說就是函數指針)。
  • arg 運行函數的參數,這里我們沒有使用參數,就直接NULL。

創建成功返回0。若不為0則說明創建線程失敗,常見的錯誤返回代碼為EAGAIN和EINVAL。前者表示系統限制創建新的線程,例如線程數目過多了;后者表示第二個參數代表的線程屬性值非法。

1.2 pthread_t

定義在 pthreadtypes.h

typedef unsigned long int pthread_t;

線程的標識符。也就是前面創建線程時候傳入的參數。當然函數參數做輸入的時候,傳的是地址。

二 線程屬性設置

同樣,我們先上示例代碼

#include <iostream>
using namespace std;

void *run(void *ptr){
        int value=*(int *)ptr;
        for(int i=0; i<3; i++) {
                sleep(1);
                cout<<"value  "<<value<<endl;
        }
        return 0;
}
int main(){
        int ret=0;
        int value=10;
        pthread_t id;
        pthread_attr_t attr;
        //初始化
        pthread_attr_init(&attr);
        //設置相關屬性
        pthread_attr_setscope (&attr,PTHREAD_SCOPE_PROCESS);
        ret=pthread_create(&id,&attr,run,&value);
        if(ret) {
                cout<<"create thread failed "<<endl;
                return 0;
        }
        pthread_join(id,NULL);
        return 0;
}

代碼相對于上一節中多了一個屬性設置和參數傳遞。涉及變量pthread_attr_t、pthread_attr_init函數等。整個流程三步走

    1. 定義屬性變量pthread_attr_t
    1. 初始化pthread_attr_t
    1. 創建線程時傳入。

下面我們具體介紹

2.1 pthread_attr_t

屬性對象主要包括是否綁定、是否分離、堆棧地址、堆棧大小、優先級。默認的屬性為非綁定、非分離、缺省1M的堆棧、與父進程同樣級別的優先級。pthread_attr_t結構的定義,定義在pthread.h

typedef struct
{
    uint32_t flags;
    void * stack_base;
    size_t stack_size;
    size_t guard_size;
    int32_t sched_policy;
    int32_t sched_priority;
} pthread_attr_t;
2.2 屬性設置
  • 屬性值不能直接設置,須使用相關函數進行操作,初始化的函數為pthread_attr_init,這個函數必須在pthread_create函數之前調用。
  • 調用相關屬性設置的方法pthread_attr_set×××即可。

下面列舉相關屬性操作函數,具體方法含義就不做介紹。

int pthread_attr_init(pthread_attr_t * attr);
int pthread_attr_destroy(pthread_attr_t * attr);

int pthread_attr_setdetachstate(pthread_attr_t * attr, int state);
int pthread_attr_getdetachstate(pthread_attr_t const * attr, int * state);

int pthread_attr_setschedpolicy(pthread_attr_t * attr, int policy);
int pthread_attr_getschedpolicy(pthread_attr_t const * attr, int * policy);

int pthread_attr_setschedparam(pthread_attr_t * attr, struct sched_param const * param);
int pthread_attr_getschedparam(pthread_attr_t const * attr, struct sched_param * param);

int pthread_attr_setstacksize(pthread_attr_t * attr, size_t stack_size);
int pthread_attr_getstacksize(pthread_attr_t const * attr, size_t * stack_size);

int pthread_attr_setstackaddr(pthread_attr_t * attr, void * stackaddr);
int pthread_attr_getstackaddr(pthread_attr_t const * attr, void ** stackaddr);

int pthread_attr_setstack(pthread_attr_t * attr, void * stackaddr, size_t stack_size);
int pthread_attr_getstack(pthread_attr_t const * attr, void ** stackaddr, size_t * stack_size);

int pthread_attr_setguardsize(pthread_attr_t * attr, size_t guard_size);
int pthread_attr_getguardsize(pthread_attr_t const * attr, size_t * guard_size);

int pthread_attr_setscope(pthread_attr_t *attr, int  scope);
int pthread_attr_getscope(pthread_attr_t const *attr);

int pthread_getattr_np(pthread_t thid, pthread_attr_t * attr);

三 線程參數傳遞

代碼還是上一節的示例代碼。
參數傳遞的是指針。我們將value的值傳入。

pthread_create(&id,&attr,run,&value);

然后進行指針變量類型轉換就可得到值。

int value=*(int *)ptr;

四 線程優先級

先上代碼,這里相對于上一節就修改了 main方法,所以只貼出部分代碼

int main(){
        int ret=0;
        int value=10;
        pthread_t id;
        pthread_attr_t attr;
        sched_param param;
        //初始化
        pthread_attr_init(&attr);
        //設置相關屬性
        pthread_attr_setscope (&attr,PTHREAD_SCOPE_PROCESS);
        //獲取線程優先級參數
        pthread_attr_getschedparam(&attr,&param);
        //設置優先級
        param.sched_priority=10;
        pthread_attr_setschedparam(&attr,&param);
        ret=pthread_create(&id,&attr,run,&value);
        if(ret) {
                cout<<"create thread failed "<<endl;
                return 0;
        }
        pthread_join(id,NULL);
        return 0;
}

主要涉及sched_parampthread_attr_setschedparampthread_attr_getschedparam等方法。優先級變量存放在結構sched_param中。用函數pthread_attr_getschedparam和函數pthread_attr_setschedparam進行存放,一般說來,我們總是先取優先級,對取得的值修改后再存放回去。

五 線程的分離狀態

線程的分離狀態決定一個線程以什么樣的方式來終止自己。
在上面的例子中,我們采用了線程的默認屬性,即為非分離狀態,這種情況下,原有的線程等待創建的線程結束。只有當pthread_join()函數返回時,創建的線程才算終止,才能釋放自己占用的系統資源。而分離線程不是這樣子的,它沒有被其他的線程所等待,自己運行結束了,線程也就終止了,馬上釋放系統資源。程序員應該根據自己的需要,選擇適當的分離狀態。設置線程分離狀態的函數為pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二個參數可選為PTHREAD_CREATE_DETACHED(分離線程)和 PTHREAD _CREATE_JOINABLE(非分離線程)。這里要注意的一點是,如果設置一個線程為分離線程,而這個線程運行又非常快,它很可能在pthread_create函數返回之前就終止了,它終止以后就可能將線程號和系統資源移交給其他的線程使用,這樣調用pthread_create的線程就得到了錯誤的線程號。要避免這種情況可以采取一定的同步措施,最簡單的方法之一是可以在被創建的線程里調用pthread_cond_timewait函數,讓這個線程等待一會兒,留出足夠的時間讓函數pthread_create返回。設置一段等待時間,是在多線程編程里常用的方法。但是注意不要使用諸如wait()之類的函數,它們是使整個進程睡眠,并不能解決線程同步的問題。

六 線程的數據處理

6.1 線程數據

在單線程的程序里,有兩種基本的數據:全局變量和局部變量。但在多線程程序里,還有第三種數據類型:線程數據(TSD: Thread-Specific Data)。它和全局變量很象,在線程內部,各個函數可以象使用全局變量一樣調用它,但它對線程外部的其它線程是不可見的。這種數據的必要性是顯而易見的。例如我們常見的變量errno,它返回標準的出錯信息。它顯然不能是一個局部變量,幾乎每個函數都應該可以調用它;但它又不能是一個全局變量,否則在A線程里輸出的很可能是B線程的出錯信息。要實現諸如此類的變量,我們就必須使用線程數據。我們為每個線程數據創建一個鍵,它和這個鍵相關聯,在各個線程里,都使用這個鍵來指代線程數據,但在不同的線程里,這個鍵代表的數據是不同的,在同一個線程里,它代表同樣的數據內容。
總結上面的理論:我們要在線程中使用全局變量,但是這個全局變量在各個線程中是獨立的。
先上代碼:

#include <iostream>
using namespace std;

//用戶訪問和獲取線程變量。所有的線程都可以訪問
pthread_key_t key;
void *run(void *ptr){
        int value=*(int *)ptr;
        int temp=1;
        //將temp的地址賦給key。
        pthread_setspecific (key, &temp);
        for(int i=0; i<3; i++) {
                //根據key獲取對應的值
                int v=*(int *)pthread_getspecific(key);
                usleep(1000*100);
                cout<<"run key value>> "<<v<<endl<<flush;
        }
        return 0;
}
void *run2(void *ptr){
        int temp=2;
        pthread_setspecific (key, &temp);
        for(int i=0; i<3; i++) {
                int v=*(int *)pthread_getspecific(key);
                usleep(1000*150);
                cout<<"run2 key value>> "<<v<<endl<<flush;
        }
        return 0;
}

int main(){
        //創建key
        pthread_key_create(&key,NULL);
        int ret=0;
        int value=10;
        pthread_t id;
        pthread_attr_t attr;
        sched_param param;
        //初始化
        pthread_attr_init(&attr);
        //設置相關屬性
        pthread_attr_setscope (&attr,PTHREAD_SCOPE_PROCESS);
        //獲取線程優先級參數
        pthread_attr_getschedparam(&attr,&param);
        //設置優先級
        param.sched_priority=10;
        pthread_attr_setschedparam(&attr,&param);
        ret=pthread_create(&id,&attr,run,&value);
        if(ret) {
                cout<<"create thread failed "<<endl;
                return 0;
        }
        pthread_t id2;
        ret=pthread_create(&id2,NULL,run2,NULL);
        if(ret) {
                cout<<"create thread 2 failed "<<endl;
                return 0;
        }
        pthread_join(id,NULL);
        pthread_join(id2,NULL);
        pthread_key_delete(key);
        return 0;
}

相關的函數和結構有pthread.hpthread_key_createpthread_key_deletepthread_key_tpthread_setspecificpthread_getspecific

  • pthread_key_create
    創建函數聲明:
int pthread_key_create(pthread_key_t *key, void (*destructor_function)(void *))

函數 pthread_key_create() 用來創建線程私有數據。該函數從 TSD 池中分配一項,將其地址值賦給 key 供以后訪問使用。

  • pthread_key_t key
    指向一個鍵值的指針 pthread_key_t的定義為typedef int pthread_key_t;不論哪個線程調用了 pthread_key_create(),所創建的 key 都是所有線程可以訪問的,但各個線程可以根據自己的需要往 key 中填入不同的值,相當于提供了一個同名而不同值的全局變量(這個全局變量相對于擁有這個變量的線程來說)。
  • destructor_function
    這是一個銷毀函數,它是可選的,可以為 NULL,為 NULL 時,則系統調用默認的銷毀函數進行相關的數據注銷。如果不為空,則在線程退出時(調用 pthread_exit() 函數)時將以 key 鎖關聯的數據作為參數調用它,以釋放分配的緩沖區,或是關閉文件流等。
  • pthread_setspecific/pthread_getspecific
    設置和獲取線程變量的值。

七 互斥鎖

互斥鎖用來保證一段時間內只有一個線程在執行一段代碼。必要性顯而易見:假設各個線程向同一個文件順序寫入數據,最后得到的結果一定是災難性的。
先上代碼,這里我們做了一個讀寫模型:

#include <iostream>
using namespace std;
char buffer;
int buffer_has_item=0;
pthread_mutex_t mutex;
void writerFunc(){
        while(1) {
                /* 鎖定互斥鎖*/
                pthread_mutex_lock (&mutex);
                buffer_has_item++;
                cout<<"write "<<buffer_has_item<<endl;
                /* 打開互斥鎖*/
                pthread_mutex_unlock(&mutex);
                usleep(1000*200);
        }
}
void *readerFunc(void *ptr){
        while(1) {
                pthread_mutex_lock(&mutex);
                if(buffer_has_item>0) {
                        cout<<"read >>>> "<<buffer_has_item<<endl;
                        buffer_has_item--;
                }
                pthread_mutex_unlock(&mutex);
                usleep(1000*500);
        }
}
int main(){
        pthread_t id;
        pthread_mutex_init (&mutex,NULL);
        pthread_create(&id, NULL,readerFunc, NULL);
        writerFunc();
        return 0;
}

結果:

[root@localhost threadDemo]# ./second 
write 1
read >>>> 1
write 1
write 2
read >>>> 2
write 2
write 3
read >>>> 3
7.1 核心流程
  • 定義一個鎖(pthread_mutex_t)
  • 初始化鎖
  • 使用pthread_mutex_lock/pthread_mutex_unlock進行鎖定和解鎖。

先看下 互斥鎖pthread_mutex_t的定義

typedef struct
{
    int volatile value;
} pthread_mutex_t;

使用前需要初始化互斥鎖pthread_mutex_init

7.2 鎖定和解鎖

pthread_mutex_lock 聲明開始用互斥鎖上鎖,此后的代碼直至調用pthread_mutex_unlock為止,均被上鎖,即同一時間只能被一個線程調用執行。當一個線程執行到pthread_mutex_lock處時,如果該鎖此時被另一個線程使用,那此線程被阻塞,即程序將等待到另一個線程釋放此互斥鎖

7.3 互斥鎖其他相關方法
  • pthread_mutexattr_setpshared設置屬性pshared。可以取:
    • PTHREAD_PROCESS_SHARED 不同進程中的線程同步
    • PTHREAD_PROCESS_PRIVATE 同進程中的不同線程同步
  • pthread_mutexattr_settype 設置互斥鎖類型
    其他:
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type);
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int  pshared);
int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared);

int pthread_mutex_init(pthread_mutex_t *mutex,
                       const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
#if 0 /* MISSING FROM BIONIC */
int pthread_mutex_timedlock(pthread_mutex_t *mutex, struct timespec*  ts);
#endif /* MISSING */

八 條件變量

前一節中我們講述了如何使用互斥鎖來實現線程間數據的共享和通信,互斥鎖一個明顯的缺點是它只有兩種狀態:鎖定和非鎖定。對于消費者,它根本不知道什么時候生產者已經生產了數據,只能通過輪詢來檢測,這就有兩個缺陷:

    1. 輪詢會占用CPU資源
    1. 輪詢的時間不好控制,可能導致消費者執行不及時。

為了解決上面這個問題,我們這樣考慮。費線程在阻塞之前要先解鎖(個人想法:消費線程已經獲得了要訪問資源的鎖,但是,即使我獲得了資源的鎖,但是由于條件暫時還不滿足,我無法用這個資源,所以我想暫時讓出這把鎖,讓之里的資源暫時為別人所用,所以在掛起前,我需要解鎖),同時還要將自己的標識符放入一個地方,以便生產線程通過這個標識符來激活自己。那新問題又來了,由于線程之間是并發/并行的。消費線程可能剛完成解鎖的操作,就被生產線程獲取到了并開始執行,這時,因為消費線程還未掛起自己,來不及將自己的標識符保存在某個位置,所以生產線程不認為有正在等待的線程(生產線程想告訴消費線程的唯一方式就是認消費線程的標識符)。這時,切換到消費線程后,消費線程將永遠的等待下去,雖然隊列中有產品,但生產線程也不會告訴消費線程。而生產線程因為隊列中有產品可能也一直的等待下去,形成了死鎖。

這里死鎖的原因很明確,就是因為消費線程在阻塞之前要先解鎖解、保存線程標識符、掛起這一系列操作不是原子操作。想要讓這一些列的操作成為原子操作,就得引入條件變量,所以不難想到使用條件變量的時候必須要“伴隨”一個互斥量。

條件變量是與互斥量相關聯的一種用于多線程之間關于共享數據狀態改變的通信機制。它將解鎖和掛起封裝成為原子操作。等待一個條件變量時,會解開與該條件變量相關的鎖,因此,使用條件變量等待的前提之一就是保證互斥量加鎖。線程醒來之后,該互斥量會被自動加鎖,所以,在完成相關操作之后需要解鎖。

用條件變量配合互斥量實現,條件變量與互斥量結合,使得在條件不滿足的情況下,能夠釋放對緩沖區的占用,使得他人能夠訪問緩沖區。當我添加滿足時,我又可以及時的加鎖之后獨占資源的完成我自己的工作。

我們先上測試代碼:

#include <iostream>
using namespace std;
int buffer_has_item=0;
pthread_mutex_t mutex;
pthread_cond_t count_nonzero;
void writerFunc(){
        while(1) {
                pthread_mutex_lock (&mutex);
                buffer_has_item+=2;
                cout<<"write "<<buffer_has_item<<endl;
                pthread_mutex_unlock(&mutex);
                //激活阻塞的讀線程
                pthread_cond_signal(&count_nonzero);
                usleep(1000*200);
        }
}
void *readerFunc(void *ptr){
        while(1) {
                pthread_mutex_lock(&mutex);
                if(buffer_has_item<=0) {
                        //暫時解鎖,把資源讓出,等待寫線程寫入。
                        //被激活后會自動加鎖
                        pthread_cond_wait( &count_nonzero, &mutex);
                }
                cout<<"read >>>> "<<buffer_has_item<<endl;
                buffer_has_item--;
                pthread_mutex_unlock(&mutex);
        }
}
int main(){
        pthread_t id;
        pthread_mutex_init (&mutex,NULL);
        pthread_create(&id, NULL,readerFunc, NULL);
        writerFunc();
        return 0;
}

主要涉及內容有pthread_cond_tpthread_cond_signalpthread_cond_wait

pthread_condattr_t的定義為

typedef long pthread_condattr_t;

用來定義條件變量。

  • pthread_cond_wait
    線程解開mutex指向的鎖并被條件變量cond阻塞。線程可以被函數pthread_cond_signal和函數pthread_cond_broadcast喚醒。,但是要注意的是,條件變量只是起阻塞和喚醒線程的作用,具體的判斷條件還需用戶給出,例如一個變量是否<=0等等

  • pthread_cond_signal
    用來釋放被阻塞在條件變量cond上的一個線程。多個線程阻塞在此條件變量上時,哪一個線程被喚醒是由線程的調度策略所決定的。

其他相關函數:

int pthread_condattr_init(pthread_condattr_t *attr);
int pthread_condattr_getpshared(pthread_condattr_t *attr, int *pshared);
int pthread_condattr_setpshared(pthread_condattr_t* attr, int pshared);
int pthread_condattr_destroy(pthread_condattr_t *attr);

int pthread_cond_init(pthread_cond_t *cond,
                      const pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond,
                           pthread_mutex_t * mutex,
                           const struct timespec *abstime);

/* BIONIC: same as pthread_cond_timedwait, except the 'abstime' given refers
 *         to the CLOCK_MONOTONIC clock instead, to avoid any problems when
 *         the wall-clock time is changed brutally
 */
int pthread_cond_timedwait_monotonic_np(pthread_cond_t         *cond,
                                        pthread_mutex_t        *mutex,
                                        const struct timespec  *abstime);

/* BIONIC: DEPRECATED. same as pthread_cond_timedwait_monotonic_np()
 * unfortunately pthread_cond_timedwait_monotonic has shipped already
 */
int pthread_cond_timedwait_monotonic(pthread_cond_t         *cond,
                                     pthread_mutex_t        *mutex,
                                     const struct timespec  *abstime);

#define HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC 1

/* BIONIC: same as pthread_cond_timedwait, except the 'reltime' given refers
 *         is relative to the current time.
 */
int pthread_cond_timedwait_relative_np(pthread_cond_t         *cond,
                                     pthread_mutex_t        *mutex,
                                     const struct timespec  *reltime);

#define HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE 1



int pthread_cond_timeout_np(pthread_cond_t *cond,
                            pthread_mutex_t * mutex,
                            unsigned msecs);

九 信號量

線程的信號量與進程間通信中使用的信號量的概念是一樣,它是一種特殊的變量,本質上是一個非負的整數計數器,它被用來控制對公共資源的訪問。它可以被增加或減少,但對其的關鍵訪問被保證是原子操作。如果一個程序中有多個線程試圖改變一個信號量的值,系統將保證所有的操作都將依次進行。
信調用函數sem_post()增加信號量。只有當信號量值大于0時,才能使用公共資源,使用后,函數sem_wait()減少信號量。
老規矩,先上代碼,跑起來再說:

#include <iostream>
#include <semaphore.h>
#include <string.h>
#include <stdio.h>
using namespace std;
//信號量
sem_t sem;
void * run(void *ptr){
        char *buf=(char *)ptr;
        while(strcmp("exit\n",buf)!=0) {
                //新號量-1
                sem_wait(&sem);
                cout<<"thread output>> "<<buf<<endl<<flush;
        }
        return 0;
}

int main(){
        char buf[100]={0};
        int ret=0;
        pthread_t id;
        //初始化信號量數量為2。默認類型,非0位進程間共享
        ret=sem_init(&sem, 0, 2);
        if(ret) {
                cout<<"sem_init failed"<<endl;
        }
        pthread_create(&id,NULL,run,buf);
        //循環從標準輸入讀(fgets會將\n也讀入)
        while(fgets(buf,sizeof(buf),stdin)) {
                if(strcmp("exit\n",buf)==0) {
                        break;
                }
                //信號量+1
                sem_post(&sem);
        }
        //清理信號量
        sem_destroy(&sem);
        return 0;
}

函數sem_trywait()和函數pthread_ mutex_trylock()起同樣的作用,它是函數sem_wait()的非阻塞版本。下面我們逐個介紹和信號量有關的一些函數,它們都在頭文件/usr/include/semaphore.h中定義。

#include <sys/cdefs.h>

__BEGIN_DECLS

typedef struct {
    volatile unsigned int  count;
} sem_t;

#define  SEM_FAILED  NULL

extern int sem_init(sem_t *sem, int pshared, unsigned int value);

extern int    sem_close(sem_t *);
extern int    sem_destroy(sem_t *);
extern int    sem_getvalue(sem_t *, int *);
extern int    sem_init(sem_t *, int, unsigned int);
extern sem_t *sem_open(const char *, int, ...);
extern int    sem_post(sem_t *);
extern int    sem_trywait(sem_t *);
extern int    sem_unlink(const char *);
extern int    sem_wait(sem_t *);

struct timespec;
extern int    sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

結果:

[root@localhost threadDemo]# ./fourth 
thread output>> 
thread output>> 
hello
thread output>> hello

come on baby
thread output>> come on baby

exit

這里需要引入新的頭文件semaphore.h

  • sem_init
    初始化信號量。該函數初始化由sem指向的信號對象,設置它的共享選項,并給它一個初始的整數值。pshared控制信號量的類型,如果其值為0,就表示這個信號量是當前進程的局部信號量,否則信號量就可以在多個進程之間共享,value為sem的初始值。調用成功時返回0,失敗返回-1.
  • sem_post ( sem_t *sem )
    該函數用于以原子操作的方式將信號量的值加1。當有線程阻塞在這個信號量上時,調用這個函數會使其中的一個線程不在阻塞,選擇機制同樣是由線程的調度策略決定的。
  • sem_wait( sem_t *sem )
    被用來阻塞當前線程直到信號量sem的值大于0,解除阻塞后將sem的值減1,表明公共資源經使用后減少。
  • sem_destroy
    該函數用于對用完的信號量的清理

semaphore.h頭文件

#ifndef _SEMAPHORE_H
#define _SEMAPHORE_H    1

#include <features.h>
#include <sys/types.h>
#ifdef __USE_XOPEN2K
# define __need_timespec
# include <time.h>
#endif

/* Get the definition for sem_t.  */
#include <bits/semaphore.h>


__BEGIN_DECLS

/* Initialize semaphore object SEM to VALUE.  If PSHARED then share it
   with other processes.  */
extern int sem_init (sem_t *__sem, int __pshared, unsigned int __value)
     __THROW;
/* Free resources associated with semaphore object SEM.  */
extern int sem_destroy (sem_t *__sem) __THROW;

/* Open a named semaphore NAME with open flags OFLAG.  */
extern sem_t *sem_open (__const char *__name, int __oflag, ...) __THROW;

/* Close descriptor for named semaphore SEM.  */
extern int sem_close (sem_t *__sem) __THROW;

/* Remove named semaphore NAME.  */
extern int sem_unlink (__const char *__name) __THROW;

/* Wait for SEM being posted.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int sem_wait (sem_t *__sem);

#ifdef __USE_XOPEN2K
/* Similar to `sem_wait' but wait only until ABSTIME.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int sem_timedwait (sem_t *__restrict __sem,
              __const struct timespec *__restrict __abstime);
#endif

/* Test whether SEM is posted.  */
extern int sem_trywait (sem_t *__sem) __THROW;

/* Post SEM.  */
extern int sem_post (sem_t *__sem) __THROW;

/* Get current value of SEM and store it in *SVAL.  */
extern int sem_getvalue (sem_t *__restrict __sem, int *__restrict __sval)
     __THROW;


__END_DECLS

#endif  /* semaphore.h */

參考博客:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 轉自:Youtherhttps://www.cnblogs.com/youtherhome/archive/201...
    njukay閱讀 1,629評論 0 52
  • 線程基礎 線程是進程的一個執行單元,執行一段程序片段,線程共享全局變量;線程的查看可以使用命令或者文件來進行查看;...
    秋風弄影閱讀 760評論 0 0
  • linux線程同步 信號燈:與互斥鎖和條件變量的主要不同在于"燈"的概念,燈亮則意味著資源可用,燈滅則意味著不可用...
    鮑陳飛閱讀 706評論 0 2
  • 摘要 線程概念,線程與進程的區別與聯系學會線程控制,線程創建,線程終止,線程等待了解線程分離與線程安全學會線程同步...
    狼之足跡閱讀 476評論 2 3
  • 一、線程的創建和調度 1.線程是程序執行的某一條指令流的映像。 為了進一步減少處理機制的空轉時間,支持多處理器及減...
    穹藍奧義閱讀 1,124評論 2 5