正確使用pthread_create,防止記憶體流失

來源:互聯網
上載者:User

原文出處:http://www.cnblogs.com/lidabo/p/5514100.html


近日,聽說pthread_create會造成記憶體流失,覺得不可思議,因此對posix(nptl)的線程建立和銷毀進行了分析。   分析結果: 如果使用不當,確實會造成記憶體流失。 產生根源 :pthread_create預設建立的線程是非detached的。 預防方式: 要麼建立detached的線程,要麼線程線程的start_routine結束之前detached,要麼join   分析過程如下:     1.查看pthread_create原始碼,核心代碼如下(nptl/pthread_create.c):

點擊(此處)摺疊或開啟 int __pthread_create_2_1 (newthread, attr, start_routine, arg)      pthread_t *newthread;      const pthread_attr_t *attr;      void *(*start_routine) (void *);       void *arg; {   STACK_VARIABLES;   const struct pthread_attr *iattr = (struct pthread_attr *) attr;   if (iattr == NULL)     /* Is this the best idea? On NUMA machines this could mean        accessing far-away memory. */     iattr = &default_attr;   struct pthread *pd = NULL;   int err = ALLOCATE_STACK (iattr, &pd);//為tcb分配記憶體   if (__builtin_expect (err != 0, 0))      /* Something went wrong. Maybe a parameter of the attributes is        invalid or we could not allocate memory. */     return err; //…… err = create_thread (pd, iattr, STACK_VARIABLES_ARGS);//正式建立線程   2.查看createthread.c(nptl/sysdeps/pthread/createthread.c)

點擊(此處)摺疊或開啟 static int  create_thread (struct pthread *pd, const struct pthread_attr *attr,            STACK_VARIABLES_PARMS) { #ifdef TLS_TCB_AT_TP   assert (pd->header.tcb != NULL); #endif //…… int res = do_clone (pd, attr, clone_flags, start_thread,                   STACK_VARIABLES_ARGS, 1);//clone一個進程 3.接著看start_thread(nptl/pthread_create.c)做了什麼

點擊(此處)摺疊或開啟 static int start_thread (void *arg) {   struct pthread *pd = (struct pthread *) arg; //……       /* Run the code the user provided. */ #ifdef CALL_THREAD_FCT       THREAD_SETMEM (pd, result, CALL_THREAD_FCT (pd));  #else       THREAD_SETMEM (pd, result, pd->start_routine (pd->arg)); //正式啟動線程的執行,並等待執行完成 #endif //…… if (IS_DETACHED (pd))     /* Free the TCB.  */     __free_tcb (pd);//如果設定detached標誌,則釋放tcb佔用的內容,否則直接返回   else if (__builtin_expect (pd->cancelhandling & SETXID_BITMASK, 0))     {       /* Some other thread might call any of the setXid functions and expect      us to reply.  In this case wait until we did that.  */       do     lll_futex_wait (&pd->setxid_futex, 0, LLL_PRIVATE);       while (pd->cancelhandling & SETXID_BITMASK);       /* Reset the value so that the stack can be reused.  */       pd->setxid_futex = 0;     } 從上面的過程,我們可以看到,如果在建立線程的時候,如果沒有設定detached標誌,則tcb記憶體永遠不會釋放   接下來,我們看看pthread_detach(npth/pthread_detach.c)做了什麼

點擊(此處)摺疊或開啟 int pthread_detach (th)      pthread_t th;  {   struct pthread *pd = (struct pthread *) th;    /* Make sure the descriptor is valid. */   if (INVALID_NOT_TERMINATED_TD_P (pd))     /* Not a valid thread handle. */     return ESRCH;   int result = 0;   /* Mark the thread as detached. */   if (atomic_compare_and_exchange_bool_acq (&pd->joinid, pd, NULL))     {        /* There are two possibilities here. First, the thread might      already be detached. In this case we return EINVAL.      Otherwise there might already be a waiter. The standard does      not mention what happens in this case. */       if (IS_DETACHED (pd))     result = EINVAL;     }    else     /* Check whether the thread terminated meanwhile. In this case we        will just free the TCB. */     if ((pd->cancelhandling & EXITING_BITMASK) != 0)       /* Note that the code in __free_tcb makes sure each thread      control block is freed only once. */       __free_tcb (pd);//經過一系列的容錯判斷,直接釋放tcb佔用的記憶體   return result; } 最後,我們看一下pthread_join(nptl/pthread_join.c)做了什麼

點擊(此處)摺疊或開啟 int pthread_join (threadid, thread_return)      pthread_t threadid;      void **thread_return; {   struct pthread *pd = (struct pthread *) threadid;   /* Make sure the descriptor is valid. */   if (INVALID_NOT_TERMINATED_TD_P (pd))     /* Not a valid thread handle. */     return ESRCH;   /* Is the thread joinable?. */   if (IS_DETACHED (pd))     /* We cannot wait for the thread. */     return EINVAL;   struct pthread *self = THREAD_SELF;   int result = 0;   /* During the wait we change to asynchronous cancellation. If we      are canceled the thread we are waiting for must be marked as      un-wait-ed for again. */   pthread_cleanup_push (cleanup, &pd->joinid);   /* Switch to asynchronous cancellation. */   int oldtype = CANCEL_ASYNC ();   if ((pd == self        || (self->joinid == pd      && (pd->cancelhandling      & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK          | TERMINATED_BITMASK)) == 0))       && !CANCEL_ENABLED_AND_CANCELED (self->cancelhandling))     /* This is a deadlock situation. The threads are waiting for each        other to finish. Note that this is a "may" error. To be 100%        sure we catch this error we would have to lock the data        structures but it is not necessary. In the unlikely case that        two threads are really caught in this situation they will        deadlock. It is the programmer's problem to figure this        out. */     result = EDEADLK;   /* Wait for the thread to finish. If it is already locked something      is wrong. There can only be one waiter. */   else if (__builtin_expect (atomic_compare_and_exchange_bool_acq (&pd->joinid,                                  self,                                  NULL), 0))     /* There is already somebody waiting for the thread. */     result = EINVAL;   else     /* Wait for the child. */     lll_wait_tid (pd->tid);   /* Restore cancellation mode. */   CANCEL_RESET (oldtype);   /* Remove the handler. */   pthread_cleanup_pop (0);   if (__builtin_expect (result == 0, 1))     {       /* We mark the thread as terminated and as joined. */       pd->tid = -1;       /* Store the return value if the caller is interested. */       if (thread_return != NULL)
    *thread_return = pd->result;//設定傳回值
          /* Free the TCB. */
      __free_tcb (pd);/釋放TCB佔用記憶體     }   return result; } 綜上,如果要保證建立線程之後,確保無記憶體流失,必須採用如下方法來規範pthread_create的使用: 方法一、建立detached的線程

點擊(此處)摺疊或開啟 void run() {      return; }                                                                                                          int main(){      pthread_t thread;      pthread_attr_t attr;      pthread_attr_init( &attr );      pthread_attr_setdetachstate(&attr,1);      pthread_create(&thread, &attr, run, 0);                 //......      return 0;  } 方法二、要麼線程線程的start_routine結束之前detached

點擊(此處)摺疊或開啟 void run() {      pthread_detach(pthread_self());  }                                                                                                          int main(){      pthread_t thread;       pthread_create(&thread, NULL, run, 0);                     //......      return 0;  } 方法三、主線程使用pthread_join

點擊(此處)摺疊或開啟 void run() {      return; }                                                                                                          int main(){      pthread_t thread;      pthread_create(&thread, NULL, run, 0);                                               //......      pthread_join(thread,NULL);     return 0;  }

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.