(轉)Linux 多線程編程---pthread_testcancel()等講解


1.   所謂線程就是“一個進程內部的一個控制序列”。也就是一個進程內部的並行的基礎!

2.    Linux進程可以看成只有一個控制線程:
      一個進程在同一時刻只做一件事情。有了多個控制線程以后,
      在程序設計時可以把進程設計成在同一時刻能夠做不止一件事,
      每個線程處理各只獨立的任務。即所謂並行!
     
3.     線程的優點:
          (1)通過為每種事件類型的處理分配單獨的線程,能夠簡化處理異步時間的代碼。
          (2)多個線程可以自動共享相同的存儲地址空間和文件描述符。
          (3)有些問題可以通過將其分解從而改善整個程序的吞吐量。
          (4)交互的程序可以通過使用多線程實現相應時間的改善,多線程可以把程序中
               處理用戶輸入輸出的部分與其它部分分開。

4.       線程的缺點:
         線程也有不足之處。編寫多線程程序需要更全面更深入的思考。
         在一個多線程程序里,因時間分配上的細微偏差或者因共享了不該共享的
         變量而造成不良影響的可能性是很大的。調試一個多線程程序也
         比調試一個單線程程序困難得多。     
     
5.    線程標識:
      我們已經知道進程有進程ID就是pid_t,那么線程也是有自己的ID的pthread_t數據類型!
      注意:實現的時候可以用一個結構來代表pthread_t數據類型,所以可以移植的操作系統
            不能把它作為整數處理。因此必須使用函數來對來對兩個線程ID進行比較。
     
6.    pthread_cancel:取消同一進程中的其他線程(注意是取消其他線程)
     
       intpthread_cancel(pthread_t tid);
      若成功返回0,否則返回錯誤編號。
      注意:pthread_cancel並不等待線程終止,它僅僅提出請求。是否真的執行還看目標線程的
            state和type的設置了!
     
7.  pthread_cleanup_push 和pthread_cleanup_pop:線程清理處理程序     
     
       voidpthread_cleanup_push( void ( * rtn ) ( void * ),void *arg );
       voidpthread_cleanup_pop( int exe );
       參數:
            rtn:  處理程序入口地址
             arg:傳遞給處理函數的參數
      線程可以安排它退出時需要調用的函數,這樣的函數稱為線程清理處理程序,線程可以建立多個清理處理程序。
      注意:此處是使用棧保存的,所以是先進后處理原則!
            如果線程是通過從啟動例程中返回而終止的,它的處理程序就不會調用。
     
      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      ATTENTION:
                  pthread_cleanup_push注冊一個回調函數,如果你的線程在對應的pthread_cleanup_pop
                  之前異常退出(return是正常退出,其他是異常),那么系統就會執行這個回調函數(回調函
                  數要做什么你自己決定)。但是如果在pthread_cleanup_pop之前沒有異常退出,
                  pthread_cleanup_pop就把對應的回調函數取消了,
     
       所以請注意:
            只有在“清理函數”的設置和取消之間有異常的退出,才會調用我們設置的“清理函數”。
            否則是不會輸出的!
            一般的觸發條件是:在之間有pthread_exit(非正常退出);或者有取消點時候!
           
       關於“取消點” (cancellation point ):
            例如執行下面代碼:
                            printf("sleep\n");
                            sleep(10);
                             printf("wake \n");
      在sleep函數中,線程睡眠,結果收到cancel信號,這時候線程從sleep中醒來,但是線程不會立刻退出。
      >>>>>:     
            函數:pthread_testcancel():
            描述:函數在運行的線程中創建一個取消點,如果cancellation無效則此函數不起作用。
            
            pthread的建議是:如果一個函數是阻塞的,那么你必須在這個函數前后建立 “ 取消點 ”, 比如:
                            printf("sleep\n");
                           pthread_testcancel();
                            sleep(10);
                            pthread_testcancel();
                             printf("wake \n");
            在執行到pthread_testcancel的位置時,線程才可能響應cancel退出進程
           
            對於一些函數來說本身就是有cancellation point 的,那么可以不管,但是大部分還是沒有的,
            所以要使用pthread_testcancel來設置一個取消點,那么也並不是對於所有的函數都是有效的,
            對於有延時的函數才是有效的,更清楚的說是有時間讓pthread_cancel響應才是OK的!
           
       附加:
            POSIX中的函數cancellation點的:
            pthread_join
            pthread_cond_wait
            thread_cond_timewait
            pthread_testcancel
            sem_wait
            sigwait       都是cancellation點.
            下面的這些系統函數也是cancellation點:
             accept
             fcntl
             open
             read
             write
             lseek
             close
             send
            sendmsg
             sendto
            connect
             recv
            recvfrom
            recvmsg
             system
            tcdrain
             fsync
             msync
             pause
             wait
            waitpid
            nanosleep
 
            其它的一些函數如果調用了上面的函數, 那么, 它們也是cancellation點.
             intpthread_setcancelstate (int STATE, int *OLDSTATE);
            用於允許或禁止處理cancellation,
            STATE可以是:PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE

             intpthread_setcanceltype (int TYPE, int *OLDTYPE);
            設置如何處理cancellation, 異步的還是推遲的.
            TYPE可以是:PTHREAD_CANCEL_ASYNCHRONOUS, PTHREAD_CANCEL_DEFERRED
              
      >>>>        
      摘錄:http://blogt.chinaunix.net/space.php?uid=23381466&do=blog&id=58787

            什么是取消點(cancelation point)?

            資料中說,根據POSIX標准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、
            pthread_cond_timedwait()、sem_wait()、sigwait()等函數以及read()、write()等會引起阻塞
            的系統調用都是Cancelation-point。而其他pthread函數都不會引起 Cancelation動作。但
            是pthread_cancel的手冊頁聲稱,由於LinuxThread庫與C庫結合得不好,因而目前C庫函
             數都不是Cancelation-point;但CANCEL信號會使線程從阻塞的系統調用中退出,並置
            EINTR錯誤碼,因此可以在需要作為Cancelation-point的系統調用前后調用pthread_testcancel(),
            從而達到POSIX標准所要求的目標,即如下代碼段:

            pthread_testcancel();
             retcode =read(fd, buffer, length);
            pthread_testcancel();

            我發現,對於C庫函數來說,幾乎可以使線程掛起的函數都會響應CANCEL信號,終止線程,
            包括sleep、delay等延時函數。                    
    本篇文章來源於 Linux公社網站(www.linuxidc.com)  原文鏈接:http://www.linuxidc.com/Linux/2012-03/57249p2.htm

 

8、示例代碼:

/*
 * ThreadCancel.c
 *
 *  Created on: Aug 17, 2013
 *      Author: root
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>
#include <errno.h>

#define NUM_THREADS 5
void* search(void *);
void print_it(void*);

pthread_t threads[NUM_THREADS];
pthread_mutex_t lock;
int tries;
int started;
int main(){
    int i, pid;
    pid = getpid();
    printf("Search for the number=%d...\n", pid);
    pthread_mutex_init(&lock,NULL);
    for(started=0;started<NUM_THREADS; started++){
        pthread_create(&threads[started], NULL, search, (void*)pid);
    }
    for(i=0; i<NUM_THREADS; i++){
        pthread_join(threads[i], NULL);
    }
    printf("It took %d tries to find the number.\n", tries);
    return 0;
}

void print_it(void * arg){
    int *try = (int *)arg;
    pthread_t tid;
    tid = pthread_self();
    printf("Thread %lx was canceled on its %d try.\n", tid, *try);
}

void * search(void * arg){
    int num = (int)arg;
    int i,j,ntries;
    pthread_t tid;
    tid = pthread_self();
    while(pthread_mutex_trylock(&lock) == EBUSY){
        pthread_testcancel();                            //thread may be canceled and call cancel function handler !
    }
    printf("current thread tid:%lx.\n", tid);
    srand((int)tid);
    printf("current thread tid:%lx, after srand method.\n", tid);
    i = rand() & 0xFFFFFF;
    pthread_mutex_unlock(&lock);
    ntries = 0;
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
    while(started < NUM_THREADS){
        sched_yield();
    }
    pthread_cleanup_push(print_it, (void*)&ntries);
    while(1){
        i = (i+1) & 0xffffff;
        ntries++;
        if(num == i){
            while(pthread_mutex_trylock(&lock) == EBUSY){
                printf("Thread %lx found the number! But not get lock!\n", tid);
                pthread_testcancel();
            }
            tries = ntries;
            printf("Thread %lx found the number!\n", tid);
            for(j=0;j<NUM_THREADS;j++){
                if(threads[j] != tid){
                    pthread_cancel(threads[j]);
                }
            }
            break;
        }

        if(ntries %100 ==0){
            pthread_testcancel();
        }
    }

    pthread_cleanup_pop(0);
    return ((void*)0);
}

   因為是多線程運行,所以運行結果可能有好幾個,運行結果(一):

   

    運行結果(二):

   


免責聲明!

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



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