線程與線程控制


原文鏈接:http://www.orlion.ga/1250/

一、線程

    同一進程的多個線程共享同一地址空間,因此Text Segment、Data Segment都是共享的,如果定義一個函數,在個線程中都可以調用,如果定義一個全局變量,在各線程中都可以訪問到,除此之外,個線程還共享一下進程資源和環境:

    • 文件描述符

    • 每種信號的處理方式(SIG_IGN、SIG_DFL或者自定義的信號處理函數)

    • 當前工作目錄

    • 用戶id和組id

    但有些資源使每個線程各有一份的:

    • 線程id

    • 上下文,包括各種寄存器的值、程序計數器和棧指針

    • 棧空間

    • errno變量

    • 信號屏蔽字

    • 調度優先級

 

二、線程控制

    1、創建線程

#include <pthread.h>

int pthread_create(pthread_t *restrict thread, const pthread_attr_t * restrict attr, void *(*start_routine)(void *), void * restrict arg);

     返回值:成功返回0,失敗返回錯誤號。其他的系統函數都是成功返回0,失敗返回-1,而錯誤號保存在全局變量errno中,而pthread庫的函數都是通過返回值返回錯誤號,雖然每個線程也都有一個errno,但這是為了兼容其他函數接口而提供的,pthread庫本身並不使用它,通過返回值返回錯誤碼更加清晰。

    在一個線程中調用pthread_create()創建線程后,當前線程從pthread_create()返回繼續往下執行,而新的線程所執行的代碼由我們傳給pthread_create的函數指針start_routine決定。start_routine函數接收一個參數,是通過ptherad_create的arg參數傳遞給它的,該參數的類型為void *,這個指針按什么類型解釋由調用者自己定義。start_routine的返回值類型也是void *,這個指針的含義同樣由調用者自己定義。start_routine返回時,這個線程就退出了,其他線程可以調用pthread_join得到start_routine的返回值,類似於父進程調用wait()得到子進程的退出狀態。

    pthread_create成功返回后,新創建的線程的id被填寫到thread參數所指向的內存單元。進程id的類型是pid_t,每個進程的id在整個系統中是唯一的,調用getpid()可以獲得當前進程的id,是一個正整數值。線程id的類型是thread_t,它只在當前進程中保證是唯一的,在不同的系統中thread_t這個類型有不同的實現,他可能是一個整數值,也可能是一個結構體,也可能是一個地址,所以不能簡單的當成整數用printf打印,調用pthread_self()可以獲得當前線程的id。

    attr參數表示線程屬性。例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_t ntid;
void printids(const char *s)
{
        pid_t      pid;
        pthread_t  tid;
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid,
               (unsigned int)tid, (unsigned int)tid);
}
void *thr_fn(void *arg)
{
        printids(arg);
        return NULL;
}
int main(void)
{
        int err;
        err = pthread_create(&ntid, NULL, thr_fn, "new thread: ");
        if (err != 0) {
                fprintf(stderr, "can't create thread: %s\n", 
strerror(err));
                exit(1);
        }
        printids("main thread:");
        sleep(1);
        return 0;
}

    編譯(gcc編譯時要加上選項 -lpthread)運行結果:

    

    可知在linux上,thread_t類型是一個地址值,屬於同一進程的多個線程調用getpid()可以得到相同的進程號,而調用pthread_self()得到的線程號各不相同

    由於pthread_create的錯誤碼不保存在errno中,因此不能直接用perror()打印錯誤信息,可以先用strerror()把錯誤碼轉成錯誤信息再打印

    如果任意一個線程調用了exit或_exit,則整個進程的所有線程都終止,由於從main函數return也相當於調用exit,為了防止新創建的線程還沒有得到執行就終止,我們在main函數return之前延時1秒,但是即使主線程等待1秒鍾,內核也不一定會調度新創建的線程執行。

 

    2、終止線程

    只終止線程而不終止進程的方法有三種

    • 從線程函數return。主線程return相當於調用了exit。

    • 一個線程可以調用pthread_cancel終止同一進程中的另一線程

    • 線程可以調用pthread_exit終止自己。

    用pthread_cancel終止一個線程分同步和異步兩種情況,比較復雜。

#include <pthread.h>

void pthread_exit(void *value_ptr);

    value_ptr是void *類型,和線程函數返回值的用法一樣,其他線程可以調用pthread_join獲得這個指針。

    pthread_exit或者return返回的指針所指向的內存單元必須是全局的或者是用malloc分配的,不能在線程函數的棧上分配,因為當其他線程得到這個返回指針時線程函數已經退出了。

#include <pthread.h>

int pthread_join(pthread_t thread, void **value_ptr);

    返回值:成功返回0,失敗返回錯誤號

    調用該函數的線程將掛起等待,直到id為thread的線程終止。thread線程一不同的方法終止,通過pthread_join得到的終止狀態是不同的,總結如下:

      • 如果thread線程通過return返回,value_ptr所指向的單元里存放的是thread線程函數的返回值。

      • 如果thread線程被別的線程調用pthread_cancel異常終止掉,value_ptr所指向的單元里存放的是常數PTHREAD_CANCELED。

      • 如果thread線程是自己調用pthread_exit終止的,value_ptr所指向的單元存放的是傳給pthread_exit的參數。

如果對thread線程的終止狀態不感興趣,可以傳NULL給value_ptr參數。

例:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void *thr_fn1(void *arg)
{
        printf("thread 1 returning\n");
        return (void *)1;
}
void *thr_fn2(void *arg)
{
        printf("thread 2 exiting\n");
        pthread_exit((void *)2);
}
void *thr_fn3(void *arg)
{
        while(1) {
                printf("thread 3 writing\n");
                sleep(1);
        }
}
int main(void)
{
        pthread_t   tid;
        void        *tret;
        pthread_create(&tid, NULL, thr_fn1, NULL);
        pthread_join(tid, &tret);
        printf("thread 1 exit code %d\n", (int)tret);
        pthread_create(&tid, NULL, thr_fn2, NULL);
        pthread_join(tid, &tret);
        printf("thread 2 exit code %d\n", (int)tret);
        pthread_create(&tid, NULL, thr_fn3, NULL);
        sleep(3);
        pthread_cancel(tid);
        pthread_join(tid, &tret);
        printf("thread 3 exit code %d\n", (int)tret);
        return 0;
}

結果:

可見在Linux的pthread庫中常數PTHREAD_CANCELED的值是-1.可以在頭文件pthread.h中找到它的定義:

#define PTHREAD_CANCELED ((void *)-1)
  • 一般情況下,流程終止后,其終止狀態一直保留到其它線程調用pthread_join獲取它的狀態為止。但是線程也可以被置為detach狀態,這樣的線程一旦終止就立刻回收它所占用的所有資源,而不保留終止狀態。不能對一個已經處於detach狀態的線程調用pthread_join,這樣的調用將返回EINVAL。對一個尚未detach的線程調用pthread_join或pthread_detach都可以把該線程置為detach狀態,也就是說不能對同一線程調用兩次pthread_join,或者如果已經對一個線程調用pthread_detach就不能再調用pthread_join了。
#include <pthread.h>

int pthread_detach(pthread_t tid);
  • 返回值:成功返回0,失敗返回錯誤號。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM