线程创建&回收:pthread_creat和pthread_join

一、pthread_creat函数

(一)定义1

位置:rt-thread > components > libc > pthreads > pthread.h

参见:RT-Thread标准版==>组件==>C库==>POSIX==>Pthread线程

总述:pthread_create是(Unix、Linux、Mac OS X)等操作系统的创建线程的函数。它的功能是创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线程函数。

pthread_create的返回值表示成功,返回0;表示出错,返回表示-1

#include 
int pthread_create(
                 pthread_t *restrict tidp,   //新创建的线程ID指向的内存单元。
                 const pthread_attr_t *restrict attr,  //线程属性(非分离/分离),默认为NULL
                 void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开始运行
                 void *restrict arg //默认为NULL。若上述函数需要参数,将参数放入结构中并将地址作为arg传入。
                  );

1.传递参数注意的问题
问题:
避免直接在传递的参数中传递发生改变的量,否则会导致结果不可测。
即使是只再创造一个单线程,也可能在线程未获取传递参数时,线程获取的变量值已经被主线程进行了修改。

通常解决方案:
重新申请一块内存,存入需要传递的参数,再将这个地址作为arg传入。

2.使用时注意防止内存泄漏
在默认情况下通过pthread_create函数创建的线程是非分离属性的,由pthread_create函数的第二个参数决定,在非分离的情况下,当一个线程结束的时候,它所占用的系统资源并没有完全真正的释放,也没有真正终止

如果要保证创建线程之后,确保无内存泄漏,必须采用如下方法来规范pthread_create的使用。

(二)定义2

pthread_create()是一个POSIX线程函数,用于创建一个新线程。

int pthread_create(pthread_t *thread, 
                   const pthread_attr_t *attr,  
                   void *(*start_routine) (void *), 
                   void *arg);

这个函数创建新线程的主要步骤为:

1. 检查传入的线程属性attr是否正确,如果为空则使用默认属性。

2. 将start_routine函数和参数arg封装为线程任务。 

3. 系统将分配资源为新线程创建环境,并保存线程ID到thread参数。

4. 新创建的线程会调用start_routine(arg)函数开始执行线程任务。 

5. 如果资源分配成功,pthread_create()返回0,否则返回错误码。

使用这个函数创建新线程的步骤: 

1. 定义线程任务函数,其原型为`void func(void )`,用于线程执行体。 

2. 确定线程任务函数的参数(如果需要)。可以将参数封装在一个结构体中。

3. 声明一个线程ID变量,类型为pthread_t。

4. 调用pthread_create()函数,传入线程ID变量的地址,线程属性,线程任务函数名和参数。

5. 如果返回0,表示线程创建成功,线程ID变量会被保存新线程的ID。否则表示创建失败。

6. 主线程和新创建的线程会并发执行各自的任务。

7. 可以通过pthread_join()函数回收线程资源,并获取线程返回值。

所以,该函数实现了创建 POSIX 线程和执行线程任务的功能。我们可以利用这个功能在程序中添加并发执行的线程,实现多任务处理

使用这个线程创建函数需要注意:

1. 线程任务函数和参数必须正确,否则线程行为未定义。

2. 必须提供线程ID变量的存储空间,否则无法获取新线程的ID。

3. 线程创建需要一定的系统资源,如果资源不足会导致创建失败。

4. 新创建的线程会与主线程并发执行,需要注意同步和互斥避免问题。

 5. 线程创建后,需要回收线程资源来避免资源泄漏,一般在线程结束时join。

6. 实现细节依赖具体操作系统,移植性较差。POSIX线程只在Unixlike系统使用。

二、pthread_join函数

int pthread_join(
               pthread_t tid, //需要等待的线程,指定的线程必须位于当前的进程中,而且不得是分离线程
               void **status  //线程tid所执行的函数返回值(返回值地址需要保证有效),其中status可以为NULL
                 );
int pthread_join(pthread_t thread, void **retval);

这个函数回收线程资源的主要步骤为:

1. 检查传入的线程ID是否正确,必须是通过pthread_create()创建的线程ID。

2. 主线程会阻塞,直到指定的线程结束运行。

3. 系统会回收指定线程使用的所有资源,如线程环境、栈空间等。

4. 如果retval非空,则保存线程返回值到其指向的变量中。 

5. pthread_join()返回0表明成功加入线程,否则返回对应错误码。 

6. 被加入的线程会真正结束运行并被系统回收。

使用这个函数回收线程资源的步骤: 

1. 在pthread_create()创建线程时,获取新线程的ID,存储在pthread_t类型变量中。

2. 在主线程中,调用pthread_join(),传入要回收的线程ID和retval变量地址。

3. 主线程会阻塞,等待指定的线程结束运行。 

4. 系统会回收该线程的所有资源,并将线程的返回值保存到retval指向的变量。

5. pthread_join()返回0,表示成功回收线程资源。主线程继续运行。

6. 被加入的线程会完全结束,不会继续执行或占用资源。

所以,该函数实现了主线程等待其他线程结束运行并回收线程资源的功能。我们必须在主线程中调用pthread_join(),否则会出现线程资源泄漏。

使用这个线程资源回收函数需要注意:

1. 传入的线程ID必须是有效的,正在运行或已经结束运行的线程ID。否则返回错误。

2. 主线程会阻塞,直到目标线程结束。如果目标线程未结束,主线程会一直阻塞。

3. retval可以为空,在这种情况下丢弃线程的返回值。

4. 每个线程必须仅被一个pthread_join()函数join,否则结果未定义。

5. 没有被其他线程join的线程会成为僵尸线程,系统资源无法回收。

6. 实现细节依赖具体操作系统,移植性较差。

三、注:在RTThread中,使用pthread_creat创建的线程,与使用系统原生API:rt_thread_create和rt_thread_init创建的线程同样,不存在主线程or分线程的区别。

//创建动态线程
rt_thread_t rt_thread_create(const char* name,
                            void (*entry)(void* parameter),
                            void* parameter,
                            rt_uint32_t stack_size,
                            rt_uint8_t priority,
                            rt_uint32_t tick);

创建静态线程
rt_err_t rt_thread_init(struct rt_thread* thread,
                        const char* name,
                        void (*entry)(void* parameter), void* parameter,
                        void* stack_start, rt_uint32_t stack_size,
                        rt_uint8_t priority, rt_uint32_t tick);

你可能感兴趣的:(thread,pthread_creat,pthread_join,线程)