<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    正確使用pthread_create,防止內存泄漏

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    近日,聽說pthread_create會造成內存泄漏,覺得不可思議,因此對posix(nptl)的線程創建和銷毀進行了分析。

      分析結果:如果使用不當,確實會造成內存泄漏。 產生根源:pthread_create默認創建的線程是非detached的。 預防方式:要么創建detached的線程,要么線程線程的start_routine結束之前detached,要么join   分析過程如下:     1.查看pthread_create源代碼,核心代碼如下(nptl/pthread_create.c):

    點擊(此處)折疊或打開

    1. int
    2. __pthread_create_2_1 (newthread, attr, start_routine, arg)
    3.      pthread_t *newthread;
    4.      const pthread_attr_t *attr;
    5.      void *(*start_routine) (void *); 
    6.      void *arg;
    7. {
    8.   STACK_VARIABLES;
    9.   const struct pthread_attr *iattr = (struct pthread_attr *) attr;
    10.   if (iattr == NULL)
    11.     /* Is this the best idea? On NUMA machines this could mean
    12.        accessing far-away memory. */
    13.     iattr = &default_attr;
    14.   struct pthread *pd = NULL;
    15.   int err = ALLOCATE_STACK (iattr, &pd);//為tcb分配內存
    16.   if (__builtin_expect (err != 0, 0)) 
    17.     /* Something went wrong. Maybe a parameter of the attributes is
    18.        invalid or we could not allocate memory. */
    19.     return err;
    20. //……
    21. err = create_thread (pd, iattr, STACK_VARIABLES_ARGS);//正式創建線程
      2.查看createthread.c(nptl/sysdeps/pthread/createthread.c)

    點擊(此處)折疊或打開

    1. static int 
    2. create_thread (struct pthread *pd, const struct pthread_attr *attr,
    3.            STACK_VARIABLES_PARMS)
    4. {
    5. #ifdef TLS_TCB_AT_TP
    6.   assert (pd->header.tcb != NULL);
    7. #endif
    8. //……
    9. int res = do_clone (pd, attr, clone_flags, start_thread,
    10.                   STACK_VARIABLES_ARGS, 1);//clone一個進程
    3.接著看start_thread(nptl/pthread_create.c)做了什么

    點擊(此處)折疊或打開

    1. static int
    2. start_thread (void *arg)
    3. {
    4.   struct pthread *pd = (struct pthread *) arg;
    5. //……
    6.       /* Run the code the user provided. */
    7. #ifdef CALL_THREAD_FCT
    8.       THREAD_SETMEM (pd, result, CALL_THREAD_FCT (pd)); 
    9. #else
    10.       THREAD_SETMEM (pd, result, pd->start_routine (pd->arg)); //正式啟動線程的執行,并等待執行完成
    11. #endif
    12. //……
    13. if (IS_DETACHED (pd))
    14.     /* Free the TCB.  */
    15.     __free_tcb (pd);//如果設置detached標志,則釋放tcb占用的內容,否則直接返回
    16.   else if (__builtin_expect (pd->cancelhandling & SETXID_BITMASK, 0))
    17.     {
    18.       /* Some other thread might call any of the setXid functions and expect
    19.      us to reply.  In this case wait until we did that.  */
    20.       do
    21.     lll_futex_wait (&pd->setxid_futex, 0, LLL_PRIVATE);
    22.       while (pd->cancelhandling & SETXID_BITMASK);
    23.       /* Reset the value so that the stack can be reused.  */
    24.       pd->setxid_futex = 0;
    25.     }
    從上面的過程,我們可以看到,如果在創建線程的時候,如果沒有設置detached標志,則tcb內存永遠不會釋放   接下來,我們看看pthread_detach(npth/pthread_detach.c)做了什么

    點擊(此處)折疊或打開

    1. int
    2. pthread_detach (th)
    3.      pthread_t th; 
    4. {
    5.   struct pthread *pd = (struct pthread *) th; 
    6.   /* Make sure the descriptor is valid. */
    7.   if (INVALID_NOT_TERMINATED_TD_P (pd))
    8.     /* Not a valid thread handle. */
    9.     return ESRCH;
    10.   int result = 0;
    11.   /* Mark the thread as detached. */
    12.   if (atomic_compare_and_exchange_bool_acq (&pd->joinid, pd, NULL))
    13.     { 
    14.       /* There are two possibilities here. First, the thread might
    15.      already be detached. In this case we return EINVAL.
    16.      Otherwise there might already be a waiter. The standard does
    17.      not mention what happens in this case. */
    18.       if (IS_DETACHED (pd))
    19.     result = EINVAL;
    20.     } 
    21.   else
    22.     /* Check whether the thread terminated meanwhile. In this case we
    23.        will just free the TCB. */
    24.     if ((pd->cancelhandling & EXITING_BITMASK) != 0)
    25.       /* Note that the code in __free_tcb makes sure each thread
    26.      control block is freed only once. */
    27.       __free_tcb (pd);//經過一系列的容錯判斷,直接釋放tcb占用的內存
    28.   return result;
    29. }
    最后,我們看一下pthread_join(nptl/pthread_join.c)做了什么

    點擊(此處)折疊或打開

    1. int
    2. pthread_join (threadid, thread_return)
    3.      pthread_t threadid;
    4.      void **thread_return;
    5. {
    6.   struct pthread *pd = (struct pthread *) threadid;
    7.   /* Make sure the descriptor is valid. */
    8.   if (INVALID_NOT_TERMINATED_TD_P (pd))
    9.     /* Not a valid thread handle. */
    10.     return ESRCH;
    11.   /* Is the thread joinable?. */
    12.   if (IS_DETACHED (pd))
    13.     /* We cannot wait for the thread. */
    14.     return EINVAL;
    15.   struct pthread *self = THREAD_SELF;
    16.   int result = 0;
    17.   /* During the wait we change to asynchronous cancellation. If we
    18.      are canceled the thread we are waiting for must be marked as
    19.      un-wait-ed for again. */
    20.   pthread_cleanup_push (cleanup, &pd->joinid);
    21.   /* Switch to asynchronous cancellation. */
    22.   int oldtype = CANCEL_ASYNC ();
    23.   if ((pd == self
    24.        || (self->joinid == pd
    25.      && (pd->cancelhandling
    26.      & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK
    27.          | TERMINATED_BITMASK)) == 0))
    28.       && !CANCEL_ENABLED_AND_CANCELED (self->cancelhandling))
    29.     /* This is a deadlock situation. The threads are waiting for each
    30.        other to finish. Note that this is a "may" error. To be 100%
    31.        sure we catch this error we would have to lock the data
    32.        structures but it is not necessary. In the unlikely case that
    33.        two threads are really caught in this situation they will
    34.        deadlock. It is the programmer's problem to figure this
    35.        out. */
    36.     result = EDEADLK;
    37.   /* Wait for the thread to finish. If it is already locked something
    38.      is wrong. There can only be one waiter. */
    39.   else if (__builtin_expect (atomic_compare_and_exchange_bool_acq (&pd->joinid,
    40.                                  self,
    41.                                  NULL), 0))
    42.     /* There is already somebody waiting for the thread. */
    43.     result = EINVAL;
    44.   else
    45.     /* Wait for the child. */
    46.     lll_wait_tid (pd->tid);
    47.   /* Restore cancellation mode. */
    48.   CANCEL_RESET (oldtype);
    49.   /* Remove the handler. */
    50.   pthread_cleanup_pop (0);
    51.   if (__builtin_expect (result == 0, 1))
    52.     {
    53.       /* We mark the thread as terminated and as joined. */
    54.       pd->tid = -1;
    55.       /* Store the return value if the caller is interested. */
    56.       if (thread_return != NULL)
    57.     *thread_return = pd->result;//設置返回值
    58.  
    59.  
    60.       /* Free the TCB. */
    61.       __free_tcb (pd);/釋放TCB占用內存
    62.     }
    63.   return result;
    64. }
    綜上,如果要保證創建線程之后,確保無內存泄漏,必須采用如下方法來規范pthread_create的使用: 方法一、創建detached的線程

    點擊(此處)折疊或打開

    1. void run() { 
    2.     return;
    3.                                                                                                        
    4. int main(){ 
    5.     pthread_t thread; 
    6.     pthread_attr_t attr; 
    7.     pthread_attr_init( &attr ); 
    8.     pthread_attr_setdetachstate(&attr,1); 
    9.     pthread_create(&thread, &attr, run, 0); 
    10.           
    11.     //...... 
    12.     return 0; 
    13. }
    方法二、要么線程線程的start_routine結束之前detached

    點擊(此處)折疊或打開

    1. void run() { 
    2.     pthread_detach(pthread_self()); 
    3.                                                                                                        
    4. int main(){ 
    5.     pthread_t thread;  
    6.     pthread_create(&thread, NULL, run, 0); 
    7.               
    8.     //...... 
    9.     return 0; 
    10. }
    方法三、主線程使用pthread_join

    點擊(此處)折疊或打開

    1. void run() { 
    2.     return;
    3.                                                                                                        
    4. int main(){ 
    5.     pthread_t thread; 
    6.     pthread_create(&thread, NULL, run, 0);  
    7.                                        
    8.     //...... 
    9.     pthread_join(thread,NULL);
    10.     return 0; 
    11. }
    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全