<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] # 線程概念 在許多經典的操作系統教科書中,總是把進程定義為程序的執行實例,它并不執行什么, 只是維護應用程序所需的各種資源,而線程則是真正的執行實體。 所以,線程是輕量級的進程(LWP:light weight process),在Linux環境下線程的本質仍是進程。 為了讓進程完成一定的工作,進程必須至少包含一個線程。 ![](https://img.kancloud.cn/e9/36/e936bb6449e10ecb945e32b3957c81ed_302x420.png) 進程,直觀點說,保存在硬盤上的程序運行以后,會在內存空間里形成一個獨立的內存體,這個內存體有自己的地址空間,有自己的堆,上級掛靠單位是操作系統。操作系統會以進程為單位,分配系統資源,所以我們也說,**進程是CPU分配資源的最小單位**。 線程存在與進程當中(進程可以認為是線程的容器),是操作系統調度執行的最小單位。說通俗點,線程就是干活的。 進程是具有一定獨立功能的程序關于某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。 線程是進程的一個實體,是 CPU 調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。 如果說進程是一個資源管家,負責從主人那里要資源的話,那么線程就是干活的苦力。一個管家必須完成一項工作,就需要最少一個苦力,也就是說,一個進程最少包含一個線程,也可以包含多個線程。苦力要干活,就需要依托于管家,所以說一個線程,必須屬于某一個進程。 進程有自己的地址空間,線程使用進程的地址空間,也就是說,進程里的資源,線程都是有權訪問的,比如說堆啊,棧啊,靜態存儲區什么的。 > 進程是操作系統分配資源的最小單位 > > 線程是操作系統調度的最小單位 # 線程函數列表安裝 命令: > sudo apt-get install manpages-posix-dev 【說明】manpages-posix-dev 包含 POSIX 的 header files 和 library calls 的用法 查看: > man -k pthread # NPTL 當 Linux 最初開發時,在內核中并不能真正支持線程。但是它的確可以通過 clone() 系統調用將進程作為可調度的實體。這個調用創建了調用進程(calling process)的一個拷貝,這個拷貝與調用進程共享相同的地址空間。LinuxThreads 項目使用這個調用來完全在用戶空間模擬對線程的支持。不幸的是,這種方法有一些缺點,尤其是在信號處理、調度和進程間同步原語方面都存在問題。另外,這個線程模型也不符合 POSIX 的要求。 要改進 LinuxThreads,非常明顯我們需要內核的支持,并且需要重寫線程庫。有兩個相互競爭的項目開始來滿足這些要求。一個包括 IBM 的開發人員的團隊開展了 NGPT(Next-Generation POSIX Threads)項目。同時,Red Hat 的一些開發人員開展了 NPTL 項目。NGPT 在 2003 年中期被放棄了,把這個領域完全留給了 NPTL。 NPTL,或稱為 Native POSIX Thread Library,是 Linux 線程的一個新實現,它克服了 LinuxThreads 的缺點,同時也符合 POSIX 的需求。與 LinuxThreads 相比,它在性能和穩定性方面都提供了重大的改進。 查看當前pthread庫版本:getconf GNU\_LIBPTHREAD\_VERSION ~~~ root@master:~ # getconf GNU_LIBPTHREAD_VERSION NPTL 2.17 ~~~ # 線程的特點 類Unix系統中,早期是沒有“線程”概念的,80年代才引入,借助進程機制實現出了線程的概念。 因此在這類系統中,進程和線程關系密切: 1) 線程是輕量級進程(light-weight process),也有PCB,創建線程使用的底層函數和進程一樣,都是clone 2) 從內核里看進程和線程是一樣的,都有各自不同的PCB. 3) 進程可以蛻變成線程 4) 在linux下,線程最是小的執行單位;進程是最小的分配資源單位 ![](https://img.kancloud.cn/7c/3f/7c3fb224933c3e459c0ff3bfabadeadc_675x462.png) 查看指定進程的LWP號: > ps ?-Lf ?pid 實際上,無論是創建進程的fork,還是創建線程的pthread\_create,底層實現都是調用同一個內核函數 clone 。 ? 如果復制對方的地址空間,那么就產出一個“進程”; ? 如果共享對方的地址空間,就產生一個“線程”。 Linux內核是不區分進程和線程的, 只在用戶層面上進行區分。所以,線程所有操作函數 pthread\_\* 是庫函數,而非系統調用。 # 線程共享資源 1) 文件描述符表 2) 每種信號的處理方式 3) 當前工作目錄 4) 用戶ID和組ID 內存地址空間 (.text/.data/.bss/heap/共享庫) # 線程非共享資源 1) 線程id 2) 處理器現場和棧指針(內核棧) 3) 獨立的棧空間(用戶空間棧) 4) errno變量 5) 信號屏蔽字 6) 調度優先級 # 線程的優缺點 **優點:** ? 提高程序并發性 ? 開銷小 ? 數據通信、共享數據方便 **缺點:** ? 庫函數,不穩定 ? 調試、編寫困難、gdb不支持 ? 對信號支持不好 優點相對突出,缺點均不是硬傷。Linux下由于實現方法導致進程、線程差別不是很大。 # 線程常用操作 ## 線程號 就像每個進程都有一個進程號一樣,每個線程也有一個線程號。進程號在整個系統中是唯一的,但線程號不同,線程號只在它所屬的進程環境中有效。 進程號用 pid\_t 數據類型表示,是一個非負整數。線程號則用 pthread\_t 數據類型來表示,Linux 使用無符號長整數表示。 有的系統在實現pthread\_t 的時候,用一個結構體來表示,所以在可移植的操作系統實現不能把它做為整數處理。 pthread\_self函數: ~~~ #include <pthread.h> ? pthread_t pthread_self(void); 功能: 獲取線程號。 參數: 無 返回值: 調用線程的線程 ID 。 ~~~ pthread\_equal函數: ~~~ int pthread_equal(pthread_t t1, pthread_t t2); 功能: 判斷線程號 t1 和 t2 是否相等。為了方便移植,盡量使用函數來比較線程 ID。 參數: t1,t2:待判斷的線程號。 返回值: 相等: 非 0 不相等:0 ~~~ ~~~ int main() { pthread_t thread_id; ? thread_id = pthread_self(); // 返回調用線程的線程ID printf("Thread ID = %lu \n", thread_id); ? if (0 != pthread_equal(thread_id, pthread_self())) { printf("Equal!\n"); } else { printf("Not equal!\n"); } ? return 0; } ~~~ 【注意】線程函數的程序在 pthread 庫中,故鏈接時要加上參數 -lpthread。 ## 線程的創建 pthread\_create函數: ~~~ #include <pthread.h> ? int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg ); 功能: 創建一個線程。 參數: thread:線程標識符地址。 attr:線程屬性結構體地址,通常設置為 NULL。 start_routine:線程函數的入口地址。 arg:傳給線程函數的參數。 返回值: 成功:0 失敗:非 0 ~~~ 在一個線程中調用pthread\_create()創建新的線程后,當前線程從pthread\_create()返回繼續往下執行,而新的線程所執行的代碼由我們傳給pthread\_create的函數指針start\_routine決定。 由于pthread\_create的錯誤碼不保存在errno中,因此不能直接用perror()打印錯誤信息,可以先用strerror()把錯誤碼轉換成錯誤信息再打印。 ~~~ // 回調函數 void *thread_fun(void * arg) { sleep(1); int num = *((int *)arg); printf("int the new thread: num = %d\n", num); ? return NULL; } ? int main() { pthread_t tid; int test = 100; ? // 返回錯誤號 int ret = pthread_create(&tid, NULL, thread_fun, (void *)&test); if (ret != 0) { printf("error number: %d\n", ret); // 根據錯誤號打印錯誤信息 printf("error information: %s\n", strerror(ret)); } ? while (1); ? return 0; } ~~~ ## 線程資源回收 pthread\_join函數: ~~~ #include <pthread.h> ? int pthread_join(pthread_t thread, void **retval); 功能: 等待線程結束(此函數會阻塞),并回收線程資源,類似進程的 wait() 函數。如果線程已經結束,那么該函數會立即返回。 參數: thread:被等待的線程號。 retval:用來存儲線程退出狀態的指針的地址。 返回值: 成功:0 失敗:非 0 ~~~ ~~~ void *thead(void *arg) { static int num = 123; //靜態變量 ? printf("after 2 seceonds, thread will return\n"); sleep(2); ? return &num; } ? int main() { pthread_t tid; int ret = 0; void *value = NULL; ? // 創建線程 pthread_create(&tid, NULL, thead, NULL); ? ? // 等待線程號為 tid 的線程,如果此線程結束就回收其資源 // &value保存線程退出的返回值 pthread_join(tid, &value); ? printf("value = %d\n", *((int *)value)); ? return 0; } ~~~ 調用該函數的線程將掛起等待,直到id為thread的線程終止。thread線程以不同的方法終止,通過pthread\_join得到的終止狀態是不同的,總結如下: 1) 如果thread線程通過return返回,retval所指向的單元里存放的是thread線程函數的返回值。 2) 如果thread線程被別的線程調用pthread\_cancel異常終止掉,retval所指向的單元里存放的是常數PTHREAD\_CANCELED。 3) 如果thread線程是自己調用pthread\_exit終止的,retval所指向的單元存放的是傳給pthread\_exit的參數。 ## 線程分離 一般情況下,線程終止后,其終止狀態一直保留到其它線程調用pthread\_join獲取它的狀態為止。但是線程也可以被置為detach狀態,這樣的線程一旦終止就立刻回收它占用的所有資源,而不保留終止狀態。 不能對一個已經處于detach狀態的線程調用pthread\_join,這樣的調用將返回EINVAL錯誤。也就是說,如果已經對一個線程調用了pthread\_detach就不能再調用pthread\_join了。 pthread\_detach函數: ~~~ #include <pthread.h> ? int pthread_detach(pthread_t thread); 功能: 使調用線程與當前進程分離,分離后不代表此線程不依賴與當前進程,線程分離的目的是將線程資源的回收工作交由系統自動來完成,也就是說當被分離的線程結束之后,系統會自動回收它的資源。所以,此函數不會阻塞。 參數: thread:線程號。 返回值: 成功:0 失敗:非0 ~~~ # 線程退出 在進程中我們可以調用exit函數或\_exit函數來結束進程,在一個線程中我們可以通過以下三種在不終止整個進程的情況下停止它的控制流。 * 線程從執行函數中返回。 * 線程調用pthread\_exit退出線程。 * 線程可以被同一進程中的其它線程取消。 pthread\_exit函數 ~~~ #include <pthread.h> ? void pthread_exit(void *retval); 功能: 退出調用線程。一個進程中的多個線程是共享該進程的數據段,因此,通常線程退出后所占用的資源并不會釋放。 參數: retval:存儲線程退出狀態的指針。 返回值:無 ~~~ ~~~ void *thread(void *arg) { static int num = 123; //靜態變量 int i = 0; while (1) { printf("I am runing\n"); sleep(1); i++; if (i == 3) { pthread_exit((void *)&num); // return &num; } } ? return NULL; } ? int main(int argc, char *argv[]) { int ret = 0; pthread_t tid; void *value = NULL; ? pthread_create(&tid, NULL, thread, NULL); ? ? pthread_join(tid, &value); printf("value = %d\n", *(int *)value); ? return 0; } ~~~ # 線程取消 ~~~ #include <pthread.h> ? int pthread_cancel(pthread_t thread); 功能: 殺死(取消)線程 參數: thread : 目標線程ID。 返回值: 成功:0 失敗:出錯編號 ~~~ 注意:線程的取消并不是實時的,而又一定的延時。需要等待線程到達某個取消點(檢查點)。 類似于玩游戲存檔,必須到達指定的場所(存檔點,如:客棧、倉庫、城里等)才能存儲進度。 殺死線程也不是立刻就能完成,必須要到達取消點。 取消點:是線程檢查是否被取消,并按請求進行動作的一個位置。通常是一些系統調用creat,open,pause,close,read,write..... 執行命令**man 7 pthreads**可以查看具備這些取消點的系統調用列表。 可粗略認為一個系統調用(進入內核)即為一個取消點。 ~~~ void *thread_cancel(void *arg) { while (1) { pthread_testcancel(); //設置取消點 } return NULL; } ? int main() { pthread_t tid; pthread_create(&tid, NULL, thread_cancel, NULL); //創建線程 ? sleep(3); //3秒后 pthread_cancel(tid); //取消tid線程 ? pthread_join(tid, NULL); ? return 0; } ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看