zoukankan      html  css  js  c++  java
  • ①thread之可连接和分离的线程 | Joinable and Detached Threads

    介绍

    默认情况下,创建的线程即是可连接的(或可结合的,joinable)。这意味着我们可以使用pthread_join()函数在任何其它线程中等待它(可连接线程)的终止:

    #include <pthread.h>
    int pthread_join(
        pthread_t thread,  //thread to join
        void **value_ptr   //store value returned by thread
    );

    这个函数将阻塞调用线程,直到目标线程thread终止。当value_ptr非空时,其中将包含着由thread线程返回的信息。在我们的文章POSIX线程参数传递 | Pthreads argument passing中看到了如何使用pthread_join()函数。被释放的内存空间仅仅是系统空间,你必须手动清除程序分配的空间,比如 malloc() 分配的空间。一个线程只能被一个线程所连接,被连接的线程必须是非分离的,否则连接会出错。所以可以看出pthread_join()有两种作用:

    1.用于等待其他线程结束:当调用 pthread_join() 时,当前线程会处于阻塞状态,直到被调用的线程结束后,当前线程才会重新开始执行。

    2.对线程的资源进行回收:如果一个线程是非分离的(默认情况下创建的线程都是非分离)并且没有对该线程使用 pthread_join() 的话,该线程结束后并不会释放其内存空间,这会导致该线程变成了“僵尸线程”。

    可连接的线程及其结果

    一个线程在我们连接(join)它之前可能就已经终止了。结果是,当一个线程是可连接时,POSIX线程系统必须保持、维护某些信息:至少是该线程的ID和返回值(the returned value)[1]。的确,正在等待连接该线程的线程是需要这些信息的。
    实际上,POSIX线程系统也可保持和可连接线程相关的其它资源信息,比如线程的栈。从POSIX的角度来看,这是完全合法的。事实上,pthread_join()保证回收与已连接线程的任何存储空间。

    Pthreads的内部实现

    维护整个线程的栈可能仅仅几个字节被确切需要,这看起来像是对系统资源的极大浪费(在一个64位的架构上,线程ID和返回值信息加起来一般也只需要16个字节)。然而,一些Pthreads实现使用这种可能性来简化和优化线程管理。实际上,线程实现需要存储和管理每个线程特有的属性,比如:

    • 线程ID,线程属性,起始入口函数,参数及返回值;
    • 线程的调度策略和优先级;
    • 信号掩码,备用信号栈;
    • 用于取消、清理缓存的标志;
    • 用于线程私有数据的键;
    • 错误编号;
    • ... ...

    让我们称线程的这种管理存储为线程控制块(the Thread Control Block, TCB)。一种常见的技术是一次性分配堆栈和TCB(比如使用单次mmap()调用),并把TCB放在栈的开始位置处[2]

     
     

    在这样类似的架构上,我们不能在没有回收TCB的情况下回收整个栈。这就是问题的关键所在:对于连接操作所需要的信息是TCB的一部分,因此整个栈空间都需要被维持。众所周知,在Linux、AIX、Sloaris等的Pthreads的实现中都采用这种技术。

    分离线程

    那当我的线程不返回任何有用的信息,并且我不必等待它的完成时,我该怎么办?仅仅为了清理的意图,无论如何还是得必须调用pthrad_join()吗?
    幸运的是,不必这样做。Pthreads提供了一种机制告诉管理系统:我正在开启这个线程,但我对连接它并没有兴趣;一旦该线程结束,请为我执行清理动作。这个操作叫做分离一个线程。我们可以按照如下方法分离一个线程:

    • 在线程的创建期间,采用detachstate线程属性
    • 在任何线程处调用pthread_detach()函数

    让我们先看一下第二种形式,也是最简单的:

    #include <pthread.h>
    int pthread_detach(
        pthread_t thread  //thread to detach
    );

    函数pthread_detach()可以被任何线程调用,特别是从线程内分离(即自家线程调用,通过pthread_self() API可以获得它自己的线程ID)。
    要想以分离状态创建一个线程,我们可以通过pthread_attr_setdetachstate()函数设置detachstate线程属性为PTHREAD_CREATE_DETACHED。如下所示:

    #include <pthread.h>
    #
    pthread_t      tid;  // thread ID
    pthread_attr_t attr; // thread attribute
    #
    // set thread detachstate attribute to DETACHED
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    #
    // create the thread 
    pthread_create(&tid, &attr, start_routine, arg);
    ...

    下述程序阐明了分离一个线程的两种方法。

    /*------------------------------- join_01.c --------------------------------*
    On Linux, compile with: 
    cc -std=c99 -pthread join_01.c -o join_01 
     #
    Check your system documentation how to enable C99 and POSIX threads on 
    other Un*x systems.
     #
    Copyright Loic Domaigne. 
    Licensed under the Apache License, Version 2.0.
    *--------------------------------------------------------------------------*/
     #
    #include <unistd.h>  // sleep()
    #include <pthread.h> 
    #include <stdio.h>   
    #include <stdlib.h>  // EXIT_SUCCESS
    #include <string.h>  // strerror() 
    #include <errno.h>
     #
    /***************************************************************************/
    /* our macro for errors checking                                           */
    /***************************************************************************/
    #define COND_CHECK(func, cond, retv, errv) 
    if ( (cond) ) 
    { 
       fprintf(stderr, "
    [CHECK FAILED at %s:%d]
    | %s(...)=%d (%s)
    
    ",
                  __FILE__,__LINE__,func,retv,strerror(errv)); 
       exit(EXIT_FAILURE); 
    }
     #
    #define ErrnoCheck(func,cond,retv)  COND_CHECK(func, cond, retv, errno)
    #define PthreadCheck(func,rc) COND_CHECK(func,(rc!=0), rc, rc)
     #
    /*****************************************************************************/
    /* thread- dummy thread                                                      */
    /*****************************************************************************/
    void*
    thread(void* ignore)
    {
       sleep(1);
       return NULL;
    }
     #
    /*****************************************************************************/
    /* detach_state. Print detachstate of a thread.                              */
    /*****************************************************************************/
    /* 
     * We find out indirectly if a thread is detached using pthread_join().  
     * If a thread is detached, then pthread_join() fails with EINVAL. 
     * Otherwise the thread is joined, and hence was joinable. 
     *
     */
    void
    detach_state(
       pthread_t   tid,  // thread to check detach status
       const char *tname // thread name
       )
    {
       int rc; // return code
     #
       rc = pthread_join(tid, NULL);
       if ( rc==EINVAL ) 
       {
          printf("%s is detached
    ", tname);
       }
       else if ( rc==0 )  
       {  
          printf("%s was joinable
    ", tname);
       }
       else 
       {
          printf("%s: pthread_join() = %d (%s)
    ", 
                 tname, rc, strerror(rc)
                );
       }
    }
     #
    /*****************************************************************************/
    /* main- main thread                                                         */
    /*****************************************************************************/
    int
    main()
    {
       pthread_t tid1, tid2, tid3; // thread 1,2 and 3.
       pthread_attr_t attr;        // thread's attribute
       int rc;  // return code
     #
       /*--------------------------------------------------------*/
       /* 1st test: normal thread creation                       */
       /*--------------------------------------------------------*/
       rc = pthread_create(&tid1, NULL, thread, NULL);
       PthreadCheck("pthread_create", rc);
       detach_state(tid1, "thread1"); // expect: joinable 
     #
       /*--------------------------------------------------------*/
       /* 2nd test: detach thread from main thread               */
       /*--------------------------------------------------------*/
       rc = pthread_create(&tid2, NULL, thread, NULL);
       PthreadCheck("pthread_create", rc);
       rc = pthread_detach(tid2);
       PthreadCheck("pthread_detach", rc);
       detach_state(tid2, "thread2"); // expect: detached
     #
       /*--------------------------------------------------------*/
       /* 3rd test: create detached thread                       */
       /*--------------------------------------------------------*/
     #
       // set detachstate attribute to DETACHED
       //
       rc=pthread_attr_init(&attr);
       PthreadCheck("pthread_attr_init", rc);
       rc=pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
       PthreadCheck("pthread_attr_setdetachstate", rc);
     #
       // create thread now 
       //
       rc = pthread_create(&tid3, &attr, thread, NULL);
       PthreadCheck("pthread_create", rc);
       detach_state(tid3, "thread3");
     #
       /*--------------------------------------------------------*/
       /* that's all folks!                                      */
       /*--------------------------------------------------------*/
       return EXIT_SUCCESS;
    }

    对于连接或分离最重要的规则就是:

    • 不要连接一个已经被连接的线程;(注:已连接的线程栈空间已被收回,再次连接将得不到可连接线程的信息)
    • 不要连接一个分离线程;(注:连接操作只可用于可连接的线程,因为分离线程栈空间的收回是由系统内部来做的)
    • 如果你分离一个线程,你不能“重连接”它;(注:调用pthread_detach()后,可连接线程的栈空间已被收回,无法再恢复)

    Notes and further Reading

    • [1] pthread & detach. A post on c.p.t. where Patrick TJ MacPhee explains that (in theory) only the thread ID and returned value should be retained.
    • [2] pthread_join and thread stack. A post on c.p.t. where Paul Pluzhnikov explains why the stack is held until the thread is joined.
    • The main thread. Article where we discuss the special semantic of the main thread.
    • Pthreads argument passing. Article where we discuss how to pass arguments between threads.
    • David R. Butenhof: Programming with POSIX Threads, Addison-Wesley, ISBN-13 978-0-201-63392-4. See in particular Section 2.1, pp 35-39 and section 5.2.3, pp 138-141.
  • 相关阅读:
    Quora的技术探索
    基于移动互联网的电子商务个性化推荐的一些思考
    想个法子找出性能差的SQL
    结束——2012年微软精英挑战赛
    sql 查询时间执行长的sql语句
    NET好书盘点
    最后两个编程题
    网络七层协议
    使用WCF创建Restful服务
    一步一步学Entity FrameWork 4
  • 原文地址:https://www.cnblogs.com/still-smile/p/13423663.html
Copyright © 2011-2022 走看看