信號量線程控制

1.線程定義
線程是進程內獨立的一條運行路線,可以稱爲輕量級進程,與同一進程內的其他線程共享內存空間及資源。因此,線程的上下文切換的開銷比創建進程小很多。
一個進程可以有多個線程,由於線程共享進程的內存空間和資源,多線程中的同步是非常重要的問題。
2.線程間的同步與互斥
針對線程共享進程內存空間及資源的問題,POSIX中提供了相應的同步機制,如互斥鎖和信號量。這兩個同步機制可以互相通過調用對方來實現,但互斥鎖更適合用於同時可用的資源是惟一的情況;信號量更適合用於同時可用的資源爲多個的情況。下面主要介紹信號量。
3.信號量
信號量就是操作系統中所用到的PV原子操作,它廣泛用於進程或線程間的同步與互斥。信號量本質上是一個非負的整數計數器。
3.1 PV原子操作的工作原理

PV 原子操作是對整數計數器信號量 sem 的操作。一次 P 操作使 sem 減一,而一次 V 操作使 sem 加一。
進程(或線程)根據信號量的值來判斷是否對公共資源具有訪問權限。當信號量 sem 的值大於等於零
時,該進程(或線程)具有公共資源的訪問權限;相反,當信號量 sem 的值小於零時,該進程(或線
程)就將阻塞直到信號量 sem 的值大於等於 0 爲止。
PV 原子操作主要用於進程或線程間的同步和互斥這兩種典型情況。若用於互斥,幾個進程(或線程)往
往只設置一個信號量 sem,它們的操作流程如圖 3.1所示。
當信號量用於同步操作時,往往會設置多個信號量,並安排不同的初始值來實現它們之間的順序執行,它
們的操作流程如圖 3.2 所示。
這裏寫圖片描述

3.2 函數說明
Linux 實現了POSIX的無名信號量,主要用於線程間的互斥與同步。

– sem_init() 創建一個信號量,並初始化它的值。
這裏寫圖片描述

–sem_wait() 和 sem_trywait()都相當於P操作,在信號量大於零時他們都能講信號量的值減一,區別在於若信號量小於零時,sem_wait()將會阻塞進程,而sem_trywait()則會立即返回。
sem_post()相當於V操作,它將信號量的值加一同時發出信號來喚醒等待的線程。
sem_getvalue()用於得到信號量的值。
sem_destroy()用於刪除信號量。
這裏寫圖片描述

3.3 實例

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>

#define THREAD_NUMBER  3              //線程數
#define REPEAT_NUMBER  3              //每個小城的小任務數
#define DELAY_TIME_LEVELS 10.0        //小任務之間的最大時間間隔

/*RAND_MAX定義在stdlib.h,其值爲2147483647*/

sem_t sem[THREAD_NUMBER];

void *thrd_func(void *arg)
{
    int thrd_num = (int)arg;
    int delay_time = 0;
    int count = 0;

    /*進行p操作*/
    sem_wait(&sem[thrd_num]);

    printf("Thread %d is starting\n",thrd_num);

    for(cont=0;count<REPEAT_NUMBER;count++)
    {
        delay_time = (int)(rand()*DELAY_TIME_LEVELS/RAND_MAX)+1;
        sleep(delay_time);
        printf("\tThread %d: job %d delay = %d\n",thrd_num, count, delay_time);
    }

    printf("Thread %d finished\n",thrd_num);

    pthread_exit(NULL);
}

int main(void)
{
    pthread_t thread[THREAD_NUMBER];
    int no=0,res;
    void * thrd_ret;

    /********    srand(unsigned int seed)     用來設置rand()產生隨機數時的隨機數種子*********/
    /** 參數seed必須是個整數,通常可以利用geypid()或time(0)的返回值來當做seed **/
    srand(time(NULL));

    for(no=0;no<THREAD_NUMBER;no++)
    {
        sem_init(&sem[no],0,0);//信號量設爲 0
        res = pthread_create(&thread[no],NULL,thrd_func,(void *)no);
        if(res !=0)
        {
            printf("Create thread %d failed\n",no);
            exit(res);
        }
    }

    printf("Create treads success\n Waiting for threads to finish...\n");

    /* 對最後創建的線程的信號量進行V 操作*/
    sem_post(&sem[THREAD_NUMBER-1]);
    for(no=THREAD_NUMBER-1;no>=0;no--)
    {
        res=pthread_join(thread[no],&thrd_ret);//等待線程結束
        if(!res)
        {
            printf("Thread %d joined\n",no);
        }
        else
        {
            printf("Thread %d join failed\n", no);
        }

        /*進行 V 操作*/
        sem_post(&sem[(no+THREAD_NUMBER-1)%THREAD_NUMBER]);
    }

    for(no=0;no<THREAD_NUMBER;no++)
    {
        /*刪除信號量*/
        sem_destroy(&sem[no]);
    }

    return 0;
}

編譯後下載到開發板運行,
這裏寫圖片描述

4. 多線程程序的編譯問題

採用線程執行的程序編譯時,如上面的程序,用一般的編譯選項 arm-linux-gcc -o threadsem thread_sem.c,出現編譯錯誤:
/tmp/ccngl594.o: In function thrd_func':
thread_sem.c:(.text+0x3c): undefined reference to
sem_wait’
/tmp/ccngl594.o: In function main':
thread_sem.c:(.text+0x180): undefined reference to
sem_init’
thread_sem.c:(.text+0x1a4): undefined reference to pthread_create'
thread_sem.c:(.text+0x1f4): undefined reference to
sem_post’
thread_sem.c:(.text+0x22c): undefined reference to pthread_join'
thread_sem.c:(.text+0x2a0): undefined reference to
sem_post’
thread_sem.c:(.text+0x2dc): undefined reference to `sem_destroy’
collect2: ld returned 1 exit status

這個是因爲pthread並非Linux系統的默認庫,編譯時注意加上-lpthread參數,以調用鏈接庫
我們再一次在終端輸入:arm-linux-gcc -o threadsem thread_sem.c -lpthread,編譯成功。

發佈了28 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章