歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> 學習Linux

APUE學習之多線程編程(一):線程的創建和銷毀,apue多線程編程

APUE學習之多線程編程(一):線程的創建和銷毀,apue多線程編程


APUE學習之多線程編程(一):線程的創建和銷毀,apue多線程編程


一、線程標識      和每個進程都有一個進程ID一樣,每個線程也有一個線程ID,線程ID是以pthread_t數據類型來表示的,在Linux中,用無符號長整型表示pthread_t,Solaris 把phread_t數據類型表示為無符號整型,FreeBSD 和Mac OS X 用一個指向pthread結構的指針來表示pthread_t數據類型。      可以使用pthread_self函數獲得自身的線程ID。   
#include <pthread.h>
 pthread_t pthread_self(void);
  二、線程創建      使用pthread_create函數創建新線程 
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_arrt_t *restrict addr, void *(*start_rtn)(void *), void *restrict arg);
     當pthread_create成功返回後,新創建線程的線程ID會被設置成tidp指向的內存單元,attr參數用於定制各種不同的線程屬性,後面再討論線程屬性,現在先把它置為null,創建一個具有默認屬性的線程。      新創建的線程從start_rtn函數開始運行,該函數接收一個無類型指針的參數arg,如果要傳給它的參數多於一個,可以把參數放到一個結構中,然後把結構的地址作為arg傳入。      線程新建後會繼承調用線程的浮點環境和屏蔽字。 例子:   #include "apue.h" #include <pthread.h> pthread_t ntid; void printids(const char *s) { pid_t pid; pthread_t tid; pid = getpid(); tid = pthread_self(); printf("%s pid %lu tid %lu (0x%lx)\n", s, (unsigned long)pid, (unsigned long)tid, (unsigned long)tid); } void *thr_fn(void *arg) { printids("new thread: "); return ((void *)0); } int main(void) { int err; err = pthread_create(&ntid, NULL, thr_fn, NULL); if (err != 0) { err_exit(err, "can't create thread"); } printids("main thread: "); sleep(1); exit(0); } View Code   這個程序有兩個特別的地方:第一,主線程需要休眠,如果主線程不休眠,主線程會退出,新線程並沒有機會運行。第二,新線程通過pthread_self(),獲得自己的線程ID。 ./a.out main thread: pid 27335 tid 3076404928 (0xb75e36c0) new thread: pid 27335 tid 3076401984 (0xb75e2b40) View Code   雖然Linux線程ID是用無符號長整型來表示的,但它們看起來更像指針。   三、線程終止      如果任意線程調用了exit,_exit,_Exit,整個進程都會終止,這個要注意。      單個線程可以通過以下三種方式退出,且不終止整個進程。      1.線程可以簡單地從啟動例程中返回,返回值是線程的退出碼。      2.線程可以被同一進程中的其他線程取消。      3.調用pthread_exit        先來看pthread_exit退出的情況。
#include <pthread.h>
void pthread_exit(void *rval_ptr);

     ravl_ptr是無類型指針,進程中的其他線程可以通過pthread_join函數獲得這個指針。

#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);

     調用線程將一直阻塞,直至指定的線程退出,rval_ptr就包含返回碼,如果線程被取消,rval_ptr指定的內存單元就設置為PTHREAD_CANCELED.可以通過調用pthread_join自動把線程置於分離狀態,如果線程已處於分離狀態,pthread_join就會調用失敗。

例子: #include "apue.h" #include <pthread.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); } int main(void) { int err; pthread_t tid1, tid2; void *tret; err = pthread_create(&tid1, NULL, thr_fn1, NULL); if (err != 0) { err_exit(err, "can't create thread1"); } err = pthread_create(&tid2, NULL, thr_fn2, NULL); if (err != 0) { err_exit(err, "can't create thread2"); } err = pthread_join(tid1, &tret); if (err != 0) { err_exit(err, "can't join thread1"); } printf("thread1 exit code:%ld\n", (long)tret); err = pthread_join(tid2, &tret); if (err != 0) { err_exit(err, "can't join thread2"); } printf("thread2 exit code:%ld\n", (long)tret); return 0; } View Code ./a.out thread 2 exiting thread 1 returning thread1 exit code:1 thread2 exit code:2 View Code   也可傳遞包含復雜消息的結構的地址,不過必須注意,這個結構所使用的內存必須在完成調用後仍是有效的。       線程也可以調用pthread_cancel函數來請求取消同一進程的其他線程
#include <pthread.h>
int pthread_cancel(pthread_t tid);

     聽著有點霸道,不過也只是請求而已,線程可以選擇忽略這個請求。

     線程可以安排它退出時需要調用的函數,這樣的函數是由pthread_cleanup_push注冊在棧中的,所以執行順序與注冊時相反。
#include <pthread.h>
void pthread_cleanup_push(void(*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);

     當線程執行以下動作時,清理函數rtn由pthread_cleanup_push函數調度

     1.調用pthread_exit時      2.響應取消請求時      3.用非零execute參數調用pthread_cleanup_pop時。 例子: #include "apue.h" #include <pthread.h> void cleanup(void *arg) { printf("cleanup: %s\n", (char *)arg); } void *thr_fn1(void *arg) { printf("thread 1 start\n"); pthread_cleanup_push(cleanup, "thread 1 first handler"); pthread_cleanup_push(cleanup, "thread 1 second handler"); printf("thread 1 push complete\n"); if (arg) { return (void *)1; } pthread_cleanup_pop(0); pthread_cleanup_pop(0); return (void *)1; } void *thr_fn2(void *arg) { printf("thread 2 start\n"); pthread_cleanup_push(cleanup, "thread 2 first handler"); pthread_cleanup_push(cleanup, "thread 2 second handler"); printf("thread 2 push complete\n"); if (arg) { pthread_exit((void *)2); } pthread_cleanup_pop(0); pthread_cleanup_pop(0); return (void *)2; } int main(void) { int err; pthread_t tid1, tid2; void *tret; err = pthread_create(&tid1, NULL, thr_fn1, (void *)1); if (err != 0) { err_exit(err, "can't create thread 1"); } err = pthread_create(&tid2, NULL, thr_fn2, (void *)1); if (err != 0) { err_exit(err, "can't create thread 2"); } err = pthread_join(tid1, &tret); if (err != 0) { err_exit(err, "can't join with thread 1"); } printf("thread 1 exit code %ld\n", (long)tret); err = pthread_join(tid2, &tret); if (err != 0) { err_exit(err, "can't join with thread 2"); } printf("thread 2 exit code %ld\n", (long)tret); return 0; } View Code  ./a.out thread 2 start thread 2 push complete cleanup: thread 2 second handler cleanup: thread 2 first handler thread 1 start thread 1 push complete thread 1 exit code 1 thread 2 exit code 2 View Code      可知如果線程是通過它的啟動例程中返回而終止的話,它的清理處理程序就不會被調用。        在默認情況下,線程的終止狀態會一直保存到對該線程調用pthread_join,如果該線程已經被分離,則底層的資源可以在線程終止時立即被收回,不用再調用pthread_join,且再調用pthread_join會出錯。
#include <pthread.h>
int pthread_detach(pthread_t tid);

http://xxxxxx/Linuxjc/1150222.html TechArticle

Copyright © Linux教程網 All Rights Reserved