linux thread Recycling

Transfer: https://www.cnblogs.com/cthon/p/9078042.html

First thread which resources to recycling, resource recovery understanding in thinking clearly after this point.

1, when the child thread creates a thread stack memory copy out from the Father;

  Thread exit variety of ways, such as return, pthread_exit, pthread_cancel the like; (detached) may be incorporated into the two types of threads (the Joinable) and separating, if the property is not set in the thread to the thread creating PTHREAD_CREATE_DETACHED, the thread defaults It can be combined. Can be combined with the threads do not release resources immediately after the thread exits must be explicitly calling pthread_join the end of the thread. Separate thread in the thread exits the system will automatically recover resources.

  For these resources, primarily processed by setting [pthread_join separation properties] and [()] are two methods.

  Wherein the separation properties and can be provided with a [pthread_attr_setdetachstat () and [] are the pthread_detach () to process].

2, the internal thread sub separate application heap (malloc, realloc, calloc) and the mutex lock resource;

  Once the cancellation request and in a pending state (i.e. after the locking, unlocking before), a thread of execution when the cancel point only if hastily, which will pthreads shared variables and objects (e.g. mutex) is placed in an inconsistent state, other threads in the process may lead to erroneous results, deadlock, and even cause the program to crash. To avoid this problem:

  Use the Cleanup function pthread_cleanup_push () and pthread_cleanup_pop () to deal with.

 

 

 

Thread exits and resource recovery

Thread exit variety of ways, such as return, pthread_exit, pthread_cancel the like; (detached) may be incorporated into the two types of threads (the Joinable) and separating, if the property is not set in the thread to the thread creating PTHREAD_CREATE_DETACHED, the thread defaults It can be combined. Can be combined with the threads do not release resources immediately after the thread exits must be explicitly calling pthread_join the end of the thread. Separate thread in the thread exits the system will automatically recover resources.

A set of several methods separate threads:

1. plus when you create a thread

pthread_attr_t attr;
pthread_t thread;
pthread_attr_init (&attr);

/ * Set attributes as a separate thread * /

pthread_attr_setdetachstat(&attr, PTHREAD_CREATE_DETACHED);
pthread_create (&thread, &attr, &thread_function, NULL);

/ * Destroy a target structure, and it can not be reused before re-initialization * /

pthread_attr_destroy (& attr);
2. call pthread_detach (pthread_self ()) in the thread;

3. The main thread calls pthread_detach (pid), pid is the child thread thread number of

Note that, provided as a separate thread can not call pthread_join after calls to be wrong
 

 

Several Second, can be combined with the threads of exit

1. Use return the child thread exits, the main thread using recycled thread pthread_join

2. Exit Sub pthread_exit threads, the main thread is used pthread_join pthread_exit received return value, and recovering the thread

3. The main thread calls pthread_cancel, then call pthread_join collection threads

    Note: To kill in the internal volume of the corresponding sub-thread handler

      pthread_cancel function conditional execution:

      1, a system call (sleep, read, write, open, etc. System Interface)

      2, pthread_testcancel (); // setting cancellation point

Thread attribute structure is as follows:

1

2

3

4

5

6

7

8

9

10

11

12

13

typedef struct

{

       int    detachstate;           //线程的分离状态

       int    schedpolicy;            // 线程调度策略

       structsched_param      schedparam;    //线程的调度参数

       int    inheritsched;          //线程的继承性

       int    scope;                 //线程的作用域

       size_t     guardsize;         //线程栈末尾的警戒缓冲区大小

       int    stackaddr_set;

       void*    stackaddr;           //线程栈的位置

       size_t     stacksize;          //线程栈的大小

}pthread_attr_t;

   

 

When pthread_create to create threads, without specifying the stack size distribution, the system will assign default values, view the default values ​​as follows:

# ulimit -s
8192
#

Above is expressed as 8M; unit KB.

Stack size can also be expressed by # ulimit -a term which stack size. ulimit -s value to re-set the size of the stack.

Generally the default stack size is 8388608; 16384 minimum stack. Bytes.

After the stack is defined as the minimum PTHREAD_STACK_MIN, comprising #include <limits.h> value can be viewed by printing. For the default values ​​can pthread_attr_getstacksize (& attr, & stack_size); stack_size printing to view.

Especially in the embedded memory is not large, the use of default values, it can lead to problems, if there is insufficient memory pthread_create returns 12, is defined as follows:

#define EAGAIN 11

#define ENOMEM 12 /* Out of memory */

The above understanding of the stack size, following on to learn how to use pthread_attr_setstacksize re-set the stack size. Look at its prototype:

#include <pthread.h>
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

attr is a variable thread attribute; STACKSIZE stack size is set. Return Value 0, -1 respectively success and failure.

Here's how to use

  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

pthread_t thread_id;

 

  int ret ,stacksize = 20480; /*thread 堆栈设置为20K,stacksize以字节为单位。*/

  pthread_attr_t attr;

  ret = pthread_attr_init(&attr); /*初始化线程属性*/<br>

  if (ret != 0)return -1;

  ret = pthread_attr_setstacksize(&attr, stacksize);

 

  if(ret != 0)return -1;

  ret = pthread_create (&thread_id, &attr, &func, NULL);

 

  if(ret != 0)return -1;

  ret = pthread_attr_destroy(&attr); /*不再使用线程属性,将其销毁*/

 

  if(ret != 0)return -1;

   

 

May need to implement a network server at the time of writing multithreaded data received multiple clients, I realized the way more stupid, endless loop waiting for connect client to create thread after connect, so in fact there is a problem that needs long-running server program, for a long time Creating recovery is a problem thread thread resources.

Linux thread resources system program is limited, the performance of a program for the number of threads that can run at the same time is limited. The default condition, after the end of a thread, the corresponding resource will not be released, so if in a program, repeatedly build thread, and the thread and the default exit, the final thread resource depletion, the process will no longer You can create a new thread.

To solve this problem, there are two ways, the system automatically releases thread resource, or release the thread resource by another thread.

After the process is running, in itself, it is also a thread, the main thread, the main thread and the main thread process establishes a shared process resources. Unlike other thread, the main thread is the end of the run, the program exits, all programs created thread will exit.

A system to automatically release
if you want at the end of the thread, the thread is released by the system resources, you need to set thread attributes to detach, separate thread is the main thread

The codes, it can be said:

pthread_t t;
pthread_attr_t a; //线程属性
pthread_attr_init(&a);  //初始化线程属性
pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);      //设置线程属性
pthread_create( &t, &a, GetAndSaveAuthviewSDRStub, (void*)lp);                   //建立线程

二 由另一个线程将该资源释放

代码上,可以这样表示:

pthread_t t;
pthread_create( NULL, NULL, GetAndSaveAuthviewSDRStub, (void*)lp);
pthread_join( t);

pthread_join( t)等待线程t退出,并释放t线程所占用的资源。

pthread_join函数会阻塞等待指定线程退出,然后回收资源,这样就有同步的功能,使一个线程等待另一个线程退出,然后才继续运行,但是对于服务器程序如果主线程在新创建的线程工作时还需要做别的事情,这种方法不是很好,就需要使用方法一

 

 linux线程执行和windows不同,pthread有两种状态joinable状态和unjoinable状态,如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。
若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。

unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己,如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。或者将线程置为joinable,然后适时调用pthread_join.

 

还有2个函数可以实现线程的分离,pthread_detach(threadid)和pthread_detach(pthread_self())。

这2个函数区别是调用他们的线程不同,没其他区别。

pthread_detach(threadid)函数的功能是使线程ID为threadid的线程处于分离状态,一旦线程处于分离状态,该线程终止时底层资源立即被回收;否则终止子线程的状态会一直保存(占用系统资源)直到主线程调用pthread_join(threadid,NULL)获取线程的退出状态。
通常是主线程使用pthread_create()创建子线程以后,一般可以调用pthread_detach(threadid)分离刚刚创建的子线程,这里的threadid是指子线程的threadid;如此以来,该子线程止时底层资源立即被回收;
被创建的子线程也可以自己分离自己,子线程调用pthread_detach(pthread_self())就是分离自己,因为pthread_self()这个函数返回的就是自己本身的线程ID。

 

资源清理

一旦又处于挂起状态的取消请求(即加锁之后,解锁之前),线程在执行到取消点时如果只是草草收场,这会将共享变量以及pthreads对象(例如互斥量)置于一种不一致状态,可能导致进程中其他线程产生错误结果、死锁,甚至造成程序崩溃。为避免这一问题:

  使用清理函数pthread_cleanup_push()和pthread_cleanup_pop()来处理,这两个函数必需成对出现,不然会编译错误。

不论是可预见的线程种植还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证种植时能顺利的释放掉自己所占用的资源,包括单独申请的对内存,特别是锁资源,就是一个必需考虑的问题。

最近常出现的情形时资源独占锁的使用:线程为了访问临界共享资源而为其加上锁,但在访问过程呗外界取消,或者发生了中断,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可见的,因此的确需要一个机制来简化用于资源释放的编程。

在POSIX线程API中提供了一个pthread_clean_push()/pthread_cleanup_pop()函数对,用于自动释放资源----从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作都将执行pthread_cleanup_push()所指定的清理函数。

 

API定义如下:

void pthread_cleanup_push(void (*routine) (void  *),  void *arg)
void pthread_cleanup_pop(int execute)

pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,

void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push()的调用将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。

有三种情况线程清理函数会被调用:

  • 线程还未执行 pthread_cleanup_pop 前,被 pthread_cancel 取消
  • 线程还未执行 pthread_cleanup_pop 前,主动执行 pthread_exit 终止
  • 线程执行 pthread_cleanup_pop,且 pthread_cleanup_pop 的参数不为 0.

注意:如果线程还未执行 pthread_cleanup_pop 前通过 return 返回,是不会执行清理函数的。

void routine()函数可参照如下定义:

1

2

3

4

5

void *cleanup(void* p)

{

        free(p);

        printf("清理函数\n");

}

 线程主动清理过程的严谨写法:

1

2

3

4

5

6

7

8

9

void thread_fun(void*p)

{<br>     p=malloc(20);

        pthread_cleanup_push(cleanup,p);

        printf("子线程\n");

        sleep(1);            //系统调用,用来响应pthread_cancel函数

        printf("是否杀死了线程\n");  //如果线程在上一句被杀死,这一句不会被打印

        pthread_exit(NULL);      //不管线程是否被杀死,这一句都会检测清理函数,并执行

        pthread_clean_pop(1);

}

  注意:在子线程中如果申请了单独的堆空间,不应用free直接清理;因为假如在线程中直接free,如果,在free之后线程被取消,清理函数被执行,则会出现重复free的情况。

  情况如下:

void thread_fun(void*p)

{

     p=malloc(20);

        pthread_cleanup_push(cleanup,p);

        printf("子线程\n");

        sleep(1);            //系统调用,用来响应pthread_cancel函数

        printf("是否杀死了线程\n");  //如果线程在上一句被杀死,这一句不会被打印

        free(p);      //不管线程是否被杀死,这一句都会检测清理函数,并执行<br>

       //加入函数在此处被cancel ,可能会出现重复free<br><br>    sleep(1);//在此处系统调用,响应pthread_cancel(),会执行清理函数<br>    return NULL;<br>    pthread_clean_pop(1);//由于上一句return,所以这一句不执行,即清理函数不会执行

}

  

pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:

#define pthread_cleanup_push(routine,arg)                                    

{

         struct _pthread_cleanup_buffer _buffer;                                  

        _pthread_cleanup_push (&_buffer, (routine), (arg));

#define pthread_cleanup_pop(execute)                                         

        _pthread_cleanup_pop (&_buffer, (execute));

}

  

可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。
在下面的例子里,当线程在"do some work"中终止时,将主动调用pthread_mutex_unlock(mut),以完成解锁动作。


以下是使用方法:

pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);

pthread_mutex_lock(&mut);

.......

pthread_mutex_unlock(&mut);

pthread_cleanup_pop(0);

  

必须要注意的是,如果线程处于PTHREAD_CANCEL_ASYNCHRONOUS状态,上述代码段就有可能出错,因为CANCEL事件有可能在
pthread_cleanup_push()和pthread_mutex_lock()之间发生,或者在pthread_mutex_unlock()和pthread_cleanup_pop()之间发生,从而导致清理函数unlock一个并没有加锁的
mutex变量,造成错误。因此,在使用清理函数的时候,都应该暂时设置成PTHREAD_CANCEL_DEFERRED模式。为此,POSIX的
Linux实现中还提供了一对不保证可移植的pthread_cleanup_push_defer_np()/pthread_cleanup_pop_defer_np()扩展函数,功能与以下
代码段相当:

{

        int oldtype;

        pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);

        pthread_cleanup_push(routine, arg);

         ......

        pthread_cleanup_pop(execute);

        pthread_setcanceltype(oldtype, NULL);

 }   

 
补充:
在线程宿主函数中主动调用return,如果return语句包含在pthread_cleanup_push()/pthread_cleanup_pop()对中,则不会引起清理函数的执行,反而会导致segment fault。
发布了17 篇原创文章 · 获赞 2 · 访问量 2万+

Guess you like

Origin blog.csdn.net/toove/article/details/98061337