有時候,goto是唯一選擇

來源:互聯網
上載者:User

一、goto情節
goto或許相當於白堊紀時期的恐龍,曾經橫行於整個地球,但是它的命運和和恐龍一樣,最後逐漸絕跡。Dijstra老師第一個對goto拍案而起,痛陳該指令的危害,正如我們現在看有些代碼的感受:寫代碼的人爽了,維護的人哭了。曾經抓住BASIC語言的尾巴,見到過早期的BASIC語言寫的程式,那個感覺和過山車類似,突然間就跑到幾丈開外。
後來大家就習慣了對goto的歧視,雖然核心中經常可以見到這些語句,但是據說是為了提高效率,同樣是有情可原,因為畢竟不是太懂,人家寫代碼的人甩我幾條街應該是沒有問題的,所以這裡也不置可否。但是這裡說的不是效率問題,而是真心必須用goto,雖然看起來非常詭異,但是這個東西的確是工程中存在的一個例子,而不是我捏造的例子。
二、pthread_cleanup_push
這個是posix線程庫中的一個標準介面,就是為了向線程註冊自己的一個遺願,如果說自己在接下來的執行中,執行到對應的pthread_cleanup_pop之前不幸被pthread_cancel取消,請系統幫我執行一下註冊的回呼函數,否則系統可能會出現不一致。畢竟一個線程掛掉之後,其它的線程可能還要繼續頑強的跑下去。
這裡最為典型的例子就是互斥鎖,當一個線程可能會獲得一個鎖,然後開始執行,但是這個執行時間比較長,並且在執行過程中被pthread_cancel殺死,那麼這個鎖算是報廢了,更為嚴重的是這裡將會成為一個線程黑洞,所有的線程執行到這裡的時候都將會掛起,並且永遠不會被喚醒。所以對於一些關鍵的操作,線程都會註冊對這個鎖的解鎖回呼函數。
進一步說,對於一個介面可能是比較關鍵,不容有任何閃失,所以它壓根不受這個鎖的限制,所以它可以跳過這個互斥鎖的擷取動作。大家可能會覺得奇怪,怎麼會有這種事情,畢竟說這個介面可能會被用來複位系統,如果說萬一哪裡出現鎖異常,那麼複位操作就會失敗,這對於互動式系統沒有關係,大家大不了按電源就好了,但是對嵌入式系統來說,它就悲劇了(如果看門狗還正常工作的話),它將成為成為一個殭屍系統,也就是雖然在跑,但是功能紊亂,並且不能複位。
綜上所述,這裡的模型代碼就是醬紫的:
[tsecer@Harry gotoOnly]$ cat gotoOnly.c
#include <pthread.h>
int doSomething(int lockfree)
{
    static pthread_mutex_t locker = PTHREAD_MUTEX_INITIALIZER;
    /*Here we will get the lock*/
    if(!lockfree)
    {
        pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock,(void*)&locker);
        pthread_mutex_lock(&locker);
    }
    /*Here may do a lot of stuff*/
    {
        extern void foo(void);
    }
    /*Here starts the cleanup*/
    if(!lockfree)
    {
        pthread_cleanup_pop(1);
    }
}
代碼的邏輯就是在某些情況下才執行pthread_cleanup_push/pthread_cleanup_pop操作,又是後並不執行,所以把它們放在一個if條件中,但是這個代碼是無法通過編譯的。
三、編譯錯誤
編譯這個代碼,提示的錯誤有些無厘頭,因為代碼裡並沒有使用任何的while操作。
[tsecer@Harry gotoOnly]$ gcc gotoOnly.c -c
gotoOnly.c: In function ‘doSomething’:
gotoOnly.c:12: error: expected ‘while’ before ‘_INIT'
所以這裡一定是預先處理之後的問題,我們看一下預先處理的結果
[tsecer@Harry gotoOnly]$ tail -n 20 gotoOnly.c.i 
# 2 "gotoOnly.c" 2
int doSomething(int lockfree)
{
 static pthread_mutex_t locker = { { 0, 0, 0, 0, 0, { 0 } } };
 /*Here we will get the lock*/
 if(!lockfree)
 {  注意:這裡的左括弧和下面相同顏色的右括弧匹配

  do { __pthread_unwind_buf_t __cancel_buf; void (*__cancel_routine) (void *) = ((void(*)(void*))pthread_mutex_unlock); void *__cancel_arg = ((void*)&locker); int not_first_call = __sigsetjmp ((struct __jmp_buf_tag
*) (void *) __cancel_buf.__cancel_jmp_buf, 0); if (__builtin_expect (not_first_call, 0)) { __cancel_routine (__cancel_arg); __pthread_unwind_next (&__cancel_buf); } __pthread_register_cancel (&__cancel_buf); 

do {;

  pthread_mutex_lock(&locker);
 }
 /*Here may do a lot of stuff*/
 {
  extern void foo(void);
 }
 /*Here starts the cleanup*/
 if(!lockfree)
 {
  do { } while (0); } while (0); __pthread_unregister_cancel (&__cancel_buf); if (1) __cancel_routine (__cancel_arg); } while (0);
 }
}
可以看到這裡已經完全錯亂了,所以編不過是最好的,如果這裡沒有錯誤而編譯出可執行檔,那問題定位起來必定將會更加複雜。
四、man手冊關於該函數的說明
 POSIX.1  permits pthread_cleanup_push() and pthread_cleanup_pop() to be
       implemented as macros that expand  to  text  containing  '{'  and  '}',
       respectively.   For  this  reason, the caller must ensure that calls to
       these functions are paired within the same function, and  at  the  same
       lexical  nesting  level
.   (In  other words, a clean-up handler is only
       established during the execution of a specified section of code.)

pthread_cleanup_pop和pthread_cleanup_push必須在同一個函數,並且必須在同一個詞法域。這是一個極強的限制,所以上面的代碼有語法錯誤,而glibc也的確是把這兩個函數作為宏來實現了。
五、goto登場
修改之後代碼
[tsecer@Harry gotoOnly]$ cat gotoOnlygoto.c 
#include <pthread.h>
int doSomething(int lockfree)
{
    static pthread_mutex_t locker = PTHREAD_MUTEX_INITIALIZER;
    /*Here we will get the lock*/
    if(lockfree)
    goto lockskipped;
        pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock,(void*)&locker);
        pthread_mutex_lock(&locker);
    lockskipped:
    /*Here may do a lot of stuff*/
    {
        extern void foo(void);
    }
    /*Here starts the cleanup*/
    if(lockfree)
    goto unlockskipped;
        pthread_cleanup_pop(1);
    unlockskipped:
    0;
}
[tsecer@Harry gotoOnly]$ gcc gotoOnlygoto.c -c
[tsecer@Harry gotoOnly]$ 

正如常言說的: goto,兇器也,不得已而用之。


轉自:http://tsecer.blog.163.com/blog/static/1501817201221882145262/

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.