POSIX 線程編程指南(二)

線程控制

創建和終止線程

 例程:

pthread_create (thread,attr,start_routine,arg)

pthread_exit (status)

pthread_cancel (thread)

pthread_attr_init (attr)

pthread_attr_destroy (attr)


int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
(注:原文通篇沒有參數類型,對於一些必要的接口,我會從http://man7.org/上面copy後粘貼與此)

 創建線程:

開始,主程序(main()函數)會包含一個默認的線程,所有其它線程必須由程序員顯式創建.

pthread_create創建新線程並讓它可執行. 這個例程可在代碼中的任意位置多次被調用執行.

pthread_create 參數:
  • thread: 新線程ID(唯一的).
  • attr:設置縣城屬性的屬性對象.可以使用一個屬性對象,也可以使用NULL表示默認值.
  • start_routine: 線程創建後要執行的函數(函數指針).
  • arg: 傳遞給start_routine的唯一參數. 必須被轉換爲void*,如果沒有要傳遞的,就使用NULL.

進程能創建的線程數量因平臺而不同。企圖超出這個限制會導致失敗或者其他意想不到的結果.

查詢和設置實現的線程限制-Linux示例.展示了查詢默認(軟件)限制並設置硬件限制的進程(包括線程)最大數量.然後驗證限制已經被重寫.
bash / ksh / sh tcsh / csh
$ ulimit -a
core file size          (blocks, -c) 16
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 255956
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

$ ulimit -Hu
7168

$ ulimit -u 7168

$ ulimit -a
core file size          (blocks, -c) 16
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 255956
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 7168
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
% limit 
cputime      unlimited
filesize     unlimited
datasize     unlimited
stacksize    unlimited
coredumpsize 16 kbytes
memoryuse    unlimited
vmemoryuse   unlimited
descriptors  1024 
memorylocked 64 kbytes
maxproc      1024

% limit maxproc unlimited

% limit
cputime      unlimited
filesize     unlimited
datasize     unlimited
stacksize    unlimited
coredumpsize 16 kbytes
memoryuse    unlimited
vmemoryuse   unlimited
descriptors  1024 
memorylocked 64 kbytes
maxproc      7168
  • 一旦被創建,線程是對等的,並且能夠創建其他線程。線程間並沒有繼承或者依賴.
Peer Threads

 線程屬性:

默認,線程被使用特定的屬性創建.其中一些屬性能被程序員通過線程屬性對象改變

初始化與銷燬屬性對象的兩個函數:pthread_attr_init 和 pthread_attr_destroy.

其餘的接口用來查詢/設置線程屬性對象中的特定屬性.這些屬性包括:
  • Detached or joinable state
  • Scheduling inheritance
  • Scheduling policy
  • Scheduling parameters
  • Scheduling contention scope
  • Stack size
  • Stack address
  • Stack guard (overflow) size

其中一些屬性稍後討論.

 Thread Binding and Scheduling:

  Question: 線程被創建後,你如何知道
 a)它在何時被操作系統調度?
 b)它將在哪個處理/內核上運行? 

如果沒有使用pthreads的調度機制,何時、何地執行線程由實現或者操作系統決定.一個健壯的程序絕不能依賴固定的線程執行順序或者特定的處理器/核心

  • Pthreads API 提供了幾個例程可被用於指定線程執行時如何被調度. 例如, 線程能以 FIFO (first-in first-out), RR (round-robin) 或者 OTHER (取決於操作系統)的方式運行. 它也提供了設置線程調度優先級值的能力

  • 本文不包含這些主題,但是,關於Linux的 “how things work” 的概覽可以在 sched_setscheduler 的用戶頁(man page)找到.

  • Pthreads API 沒有提供綁定線程到特定cpus/cores的例程(接口). 但是,一些本地實現可能包含此功能,比如提供非標準的 pthread_setaffinity_np例程. 注意,名字中的 “_np” 意思是"non-portable".

  • 操作系統也可能提供了方法. 例如,linux提供了 sched_setaffinity 例程.

 Terminating Threads & pthread_exit():

線程能以如下幾種方式終止(terminated):

線程正常從它的起始例程返回,工作完成.

線程調用 pthread_exit 子例程- 不管它是否完工

線程被其他線程通過pthread_cancel 例程取消.

整個進程通過調用 exec() 或者 exit() 終止

如果 main() 先完成,不用自己顯式調用 pthread_exit

pthread_exit() 例程允許程序員指定一個可選的終結狀態參數. 可選參數在典型情況下會返回給 “joining” 此線程的線程..

對於正常執行結束的子例程,可以不調用 pthread_exit() – 除非你想取回可選的狀態碼..

Cleanup: pthread_exit() 例程不會關閉文件; 任何在線程中打開的文件在線程結束後仍將保持打開狀態.

Discussion on calling pthread_exit() from main():
  • 如果main()在它產生的線程前結束而沒有顯式調用pthread_exit(),那麼肯定會有問題.它創建的所有線程都會終結,因爲main()完成了,不會繼續存在以支持哪些線程.
  • 讓 main() 在最後顯式調用 pthread_exit() , main() 會阻塞並保持到它所創建的線程都完成.

Example: Pthread Creation and Termination

下面的示例使用 pthread_create() 例程創建了5個線程. 每個線程都打印 "Hello World!" 消息, 然後調用 pthread_exit()終結.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS	5

void *PrintHello(void *threadid)
{
   long tid;
   tid = (long)threadid;
   printf("Hello World! It's me, thread #%ld!\n", tid);
   pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
   pthread_t threads[NUM_THREADS];
   int rc;
   long t;
   for(t=0;t<NUM_THREADS;t++){
     printf("In main: creating thread %ld\n", t);
     rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
     if (rc){
       printf("ERROR; return code from pthread_create() is %d\n", rc);
       exit(-1);
       }
     }

   /* Last thing that main() should do */
   pthread_exit(NULL);
}
輸出:
In main: creating thread 0
In main: creating thread 1
Hello World! It's me, thread #0!
In main: creating thread 2
Hello World! It's me, thread #1!
Hello World! It's me, thread #2!
In main: creating thread 3
In main: creating thread 4
Hello World! It's me, thread #3!
Hello World! It's me, thread #4!

傳遞參數給線程

pthread_create() 例程允許程序員傳遞一個參數給線程的開始例程. 對於必須傳遞多個參數的情況,可以創建一個包含所有參數的結構體來克服這個限制, 並在pthread_create()例程中傳遞一個指向這個結構體實例的指針.

所有的參數必須以引用方式傳遞(傳地址)並轉換成 (void *).
  Question:如何安全傳遞數據給新創建的線程(考慮到它們非確定性的啓動和調度)? 

確保所有的數據都是線程安全的,也就是說,不能在該線程不知情的情況下被別的線程修改

 Example 1 - Thread Argument Passing (傳遞簡單的整形變量)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS	8

char *messages[NUM_THREADS];

void *PrintHello(void *threadid)
{
   long taskid;

   sleep(1);
   taskid = (long) threadid;
   printf("Thread %d: %s\n", taskid, messages[taskid]);
   pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
long taskids[NUM_THREADS];
int rc, t;

messages[0] = "English: Hello World!";
messages[1] = "French: Bonjour, le monde!";
messages[2] = "Spanish: Hola al mundo";
messages[3] = "Klingon: Nuq neH!";
messages[4] = "German: Guten Tag, Welt!"; 
messages[5] = "Russian: Zdravstvuyte, mir!";
messages[6] = "Japan: Sekai e konnichiwa!";
messages[7] = "Latin: Orbis, te saluto!";

for(t=0;t<NUM_THREADS;t++) {
  taskids[t] = t;
  printf("Creating thread %d\n", t);
  rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);
  if (rc) {
    printf("ERROR; return code from pthread_create() is %d\n", rc);
    exit(-1);
    }
  }

pthread_exit(NULL);
}
輸出:
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Thread 0: English: Hello World!
Thread 1: French: Bonjour, le monde!
Thread 2: Spanish: Hola al mundo
Thread 3: Klingon: Nuq neH!
Thread 4: German: Guten Tag, Welt!
Thread 5: Russian: Zdravstvytye, mir!
Thread 6: Japan: Sekai e konnichiwa!
Thread 7: Latin: Orbis, te saluto!
 Example 2 - Thread Argument Passing(通過結構體傳遞多個變量)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS	8

char *messages[NUM_THREADS];

struct thread_data
{
   int	thread_id;
   int  sum;
   char *message;
};

struct thread_data thread_data_array[NUM_THREADS];

void *PrintHello(void *threadarg)
{
   int taskid, sum;
   char *hello_msg;
   struct thread_data *my_data;

   sleep(1);
   my_data = (struct thread_data *) threadarg;
   taskid = my_data->thread_id;
   sum = my_data->sum;
   hello_msg = my_data->message;
   printf("Thread %d: %s  Sum=%d\n", taskid, hello_msg, sum);
   pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
int *taskids[NUM_THREADS];
int rc, t, sum;

sum=0;
messages[0] = "English: Hello World!";
messages[1] = "French: Bonjour, le monde!";
messages[2] = "Spanish: Hola al mundo";
messages[3] = "Klingon: Nuq neH!";
messages[4] = "German: Guten Tag, Welt!"; 
messages[5] = "Russian: Zdravstvytye, mir!";
messages[6] = "Japan: Sekai e konnichiwa!";
messages[7] = "Latin: Orbis, te saluto!";

for(t=0;t<NUM_THREADS;t++) {
  sum = sum + t;
  thread_data_array[t].thread_id = t;
  thread_data_array[t].sum = sum;
  thread_data_array[t].message = messages[t];
  printf("Creating thread %d\n", t);
  rc = pthread_create(&threads[t], NULL, PrintHello, (void *) 
       &thread_data_array[t]);
  if (rc) {
    printf("ERROR; return code from pthread_create() is %d\n", rc);
    exit(-1);
    }
  }
pthread_exit(NULL);
}
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Thread 0: English: Hello World!  Sum=0
Thread 1: French: Bonjour, le monde!  Sum=1
Thread 2: Spanish: Hola al mundo  Sum=3
Thread 3: Klingon: Nuq neH!  Sum=6
Thread 4: German: Guten Tag, Welt!  Sum=10
Thread 5: Russian: Zdravstvytye, mir!  Sum=15
Thread 6: Japan: Sekai e konnichiwa!  Sum=21
Thread 7: Latin: Orbis, te saluto!  Sum=28
 Example 3 - Thread Argument Passing (錯誤的傳遞方式)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS     8

void *PrintHello(void *threadid)
{
   long taskid;
   sleep(1);
   taskid = *(long *)threadid;
   printf("Hello from thread %ld\n", taskid);
   pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
int rc;
long t;

for(t=0;t<NUM_THREADS;t++) {
  printf("Creating thread %ld\n", t);
  rc = pthread_create(&threads[t], NULL, PrintHello, (void *) &t);
  if (rc) {
    printf("ERROR; return code from pthread_create() is %d\n", rc);
    exit(-1);
    }
   }

pthread_exit(NULL);
}
輸出:
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章