天天看點

89-線程屬性

一直以來建立線程的時候,都将線程屬性參數 pthread_attr_t 設定為 NULL. 通常來說這都能滿足需求。本文将簡要介紹 pthread_attr_t 中包含的屬性。

1. pthread_attr_t

pthread_create 函數其中一個參數為此類型,表示将建立指定類型的線程,通常,該類型是一個結構體,包含下面這些屬性:

  • Detach state: 線程的分離屬性
  • Scope: 線程競争域
  • Inherit scheduler: 排程繼承性
  • Scheduling policy: 排程政策
  • Scheduling priority: 線程優先級
  • Guard size: 警界緩沖區大小,預設是一個 PAGE_SIZE(4096KB)
  • Stack address: 棧起始位址(棧最低記憶體位址)
  • Stack size: 棧大小

我隻是把這些東西列出來而已,是以各位同學請不要拍磚,我知道大多數屬性你是看不懂的。

實際上,這裡大多數屬性你最好不要去更改,我們能夠使用的,一般就是線程的分離屬性,對于其它的東西,隻有在你了解了線程運作機制以及排程政策之後才能夠看的懂。

不過,下面還是簡單介紹一下不同屬性要幹什麼事吧。

2. 不同屬性的作用

2.1 Detach state

這個屬性是線程的分離屬性,它表示線程結束的時候,是否回收資源(比如它所使用的棧、線程結構體、線程過程函數的傳回值等等)。

分離屬性有兩種情況:

  • ​PTHREAD_CREATE_JOINABLE​

    ​: 這種情況是預設的,表示結束的時候不回收資源,需要調用 pthread_join 函數顯式回收,這種情況我們早已經學會了。
  • ​PTHREAD_CREATE_DETACHED​

    ​: 表示線程結束了,直接釋放所占用的資源,這種情況下不可再使用 pthread_join 函數去 wait 它了。

2.2 Scope

線程競争域,該屬性有兩種情況:

  • ​PTHREAD_SCOPE_SYSTEM​

    ​:表示線程和整個作業系統中的線程進行競争,比如 CPU (在排程的時候,考慮系統中所有的線程).
  • ​PTHREAD_SCOPE_PROCESS​

    ​: 表示線程隻和目前程序内的線程進行競争,比如線程排程的時候,隻考慮目前程序中的線程。

在 Linux 中,隻支援​

​PTHREAD_SCOPE_SYSTEM​

​,因為 Linux 中實作的線程,實際上就是輕量級程序。

用于設定和擷取 scope 值的函數如下:

int pthread_attr_setscope(pthread_attr_t *attr,int scope);

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

2.3 Inherit scheduler

排程繼承性,該屬性有兩種情況:

  • ​PTHREAD_INHERIT_SCHED​

    ​: 繼承程序的排程政策
  • ​PTHREAD_EXPLICIT_SCHED​

    ​: 不使用繼承的排程政策,而是使用自己提供的排程政策。

用于設定和擷取 inherit scheduler 值的函數如下:

int pthread_attr_getinheritsched(const pthread_attr_t *attr,int *inheritsched);

int pthread_attr_setinheritsched(pthread_attr_t *attr,int      

2.4 Scheduling policy

排程政策,該屬性有以下幾個值:

  • ​SCHED_OTHER​

  • ​SCHED_BATCH​

  • ​SCHED_IDLE​

  • ​SCHED_FIFO​

  • ​SCHED_RR​

這些排程政策可分成兩大類:

  • 普通排程政策(或者叫非實時排程政策),主要包括​

    ​SCHED_OTHER​

    ​​、​

    ​SCHED_BATCH​

    ​​和​

    ​SCHED_IDLE​

    ​ 這三種。
  • 實時排程政策,主要包括​

    ​SCHED_FIFO​

    ​​和​

    ​SCHED_RR​

    ​。

關于這些排程政策的含義,本文不詳細讨論。

用于設定和擷取 policy 值的函數如下:

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

int pthread_attr_setschedpolicy(pthread_attr_t *attr, int      

2.5 Scheduling priority

排程優先級,通常該值通過下面的函數設定和擷取。

pthread_attr_setschedparm(pthread_attr_t *attr, 
  const struct sched_param *param)

pthread_attr_getschedparm(pthread_attr_t *attr, 
  struct sched_param *param)      
struct sched_param {
  ...
  int sched_priority;
  ...      

對于普通排程政策來說,該值隻能是 0. 對于動态優先級來說,該值的範圍是 [1,99],但是 POSIX 規定,該範圍需要通過 sched_get_priority_min 和 sched_get_priority_max 函數來擷取。在具體實作中,最小是 32.

2.6 Guard size

警界緩沖區大小,預設是一個 PAGE_SIZE(4096KB). 用來設定和得到線程棧末尾的警戒緩沖區大小。如果線程棧運作到了警界區,就會收到信号。

可以使用下面兩個函數來對其進行設定和擷取:

int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);

int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);      

2.7 Stack address and Stack size

Stack address 表示棧起始位址(棧最低記憶體位址),一般來說在我們的 x86 或 x64 處理器上,棧都是往低位址的方向生長,是以該值一般表示棧的末尾(棧頂)。

Stack size 表示棧的大小。

可以使用下面兩個函數來對其進行設定和擷取:

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

int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);      

對于棧的大小,還可以使用下面的函數:

int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);      

3. 實驗

程式 threadattr 可以用來檢視線程屬性。該程式可以通過指令行傳入一個參數,表示棧大小。另外,帶參數的情況下,還會修改線程很多預設屬性。

3.1 程式清單

// threadattr.c
#define _GNU_SOURCE     /* To get pthread_getattr_np() declaration */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define handle_error_en(en, msg) \
  do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

static void display_pthread_attr(pthread_attr_t *attr, char *prefix)
{
  int s, i;
  size_t v;
  void *stkaddr;
  struct sched_param sp;

  s = pthread_attr_getdetachstate(attr, &i);
  if (s != 0)
    handle_error_en(s, "pthread_attr_getdetachstate");
  printf("%sDetach state        = %s\n", prefix,
      (i == PTHREAD_CREATE_DETACHED) ? "PTHREAD_CREATE_DETACHED" :
      (i == PTHREAD_CREATE_JOINABLE) ? "PTHREAD_CREATE_JOINABLE" :
      "???");

  s = pthread_attr_getscope(attr, &i);
  if (s != 0)
    handle_error_en(s, "pthread_attr_getscope");
  printf("%sScope               = %s\n", prefix,
      (i == PTHREAD_SCOPE_SYSTEM)  ? "PTHREAD_SCOPE_SYSTEM" :
      (i == PTHREAD_SCOPE_PROCESS) ? "PTHREAD_SCOPE_PROCESS" :
      "???");

  s = pthread_attr_getinheritsched(attr, &i);
  if (s != 0)
    handle_error_en(s, "pthread_attr_getinheritsched");
  printf("%sInherit scheduler   = %s\n", prefix,
      (i == PTHREAD_INHERIT_SCHED)  ? "PTHREAD_INHERIT_SCHED" :
      (i == PTHREAD_EXPLICIT_SCHED) ? "PTHREAD_EXPLICIT_SCHED" :
      "???");

  s = pthread_attr_getschedpolicy(attr, &i);
  if (s != 0)
    handle_error_en(s, "pthread_attr_getschedpolicy");

  printf("%sScheduling policy   = %s\n", prefix,
             (i == SCHED_OTHER) ? "SCHED_OTHER" :
               (i == SCHED_FIFO)  ? "SCHED_FIFO" :
               (i == SCHED_RR)    ? "SCHED_RR" :
               "???");

  s = pthread_attr_getschedparam(attr, &sp);
  if (s != 0)
    handle_error_en(s, "pthread_attr_getschedparam");
  printf("%sScheduling priority = %d\n", prefix, sp.sched_priority);

  s = pthread_attr_getguardsize(attr, &v);
  if (s != 0)
    handle_error_en(s, "pthread_attr_getguardsize");
  printf("%sGuard size          = %d, prefix, v);

  s = pthread_attr_getstack(attr, &stkaddr, &v);
  if (s != 0)
    handle_error_en(s, "pthread_attr_getstack");
  printf("%sStack address       = %p\n", prefix, stkaddr);
  printf("%sStack size          = 0x%x, prefix, v);
}

static void * thread_start(void *arg)
{
  int s;
  pthread_attr_t gattr;

  /* pthread_getattr_np() is a non-standard GNU extension that
   *               retrieves the attributes of the thread specified in its
   *                             first argument */

  s = pthread_getattr_np(pthread_self(), &gattr);
  if (s != 0)
    handle_error_en(s, "pthread_getattr_np");

  printf("Thread attributes:\n");
  display_pthread_attr(&gattr, "\t");

  exit(EXIT_SUCCESS);         /* Terminate all threads */
}

int main(int argc, char *argv[])
{
  pthread_t thr;
  pthread_attr_t attr;
  pthread_attr_t *attrp;      /* NULL or &attr */
  int s;

  attrp = NULL;

  /* If a command-line argument was supplied, use it to set the
   * stack-size attribute and set a few other thread attributes,
   * and set attrp pointing to thread attributes object */

  if (argc > 1) {
    int stack_size;
    void *sp;

    attrp = &attr;

    s = pthread_attr_init(&attr);
    if (s != 0)
      handle_error_en(s, "pthread_attr_init");

    s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (s != 0)
      handle_error_en(s, "pthread_attr_setdetachstate");

    s = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    if (s != 0)
      handle_error_en(s, "pthread_attr_setinheritsched");

    stack_size = strtoul(argv[1], NULL, 0);

    s = posix_memalign(&sp, sysconf(_SC_PAGESIZE), stack_size);
    if (s != 0)
      handle_error_en(s, "posix_memalign");

    printf("posix_memalign() allocated at %p\n", sp);

    s = pthread_attr_setstack(&attr, sp, stack_size);
    if (s != 0)
      handle_error_en(s, "pthread_attr_setstack");
  }

  s = pthread_create(&thr, attrp, &thread_start, NULL);
  if (s != 0)
    handle_error_en(s, "pthread_create");
  if (attrp != NULL) {
    s = pthread_attr_destroy(attrp);
    if (s != 0)
      handle_error_en(s, "pthread_attr_destroy");
  }

  pause();    /* Terminates when other thread calls exit() */      

3.2 編譯和運作

  • 編譯
$ gcc threadattr.c -o threadattr -lphtread      
  • 不帶參數運作
$ ./threadattr      
89-線程屬性

圖1 不帶參數運作

  • 帶參數運作
$ ./threadattr 0x3000000      
89-線程屬性

4. 總結

  • 知道線程屬性包含哪些内容
  • 了解分離屬性的作用

繼續閱讀