Linux线程的生命周期

《Unix环境高级编程》、《Posix多线程程序设计》学习笔记和重点摘要。

一、线程的4种状态

状态
含义
就绪(Ready)
线程能够运行,但在等待可用的处理器。可能刚刚创建启动,或刚刚从阻塞中恢复,或者被其他线程抢占
运行(Running)
线程正在运行。在多处理器系统中,可能有多个线程处于运行态
阻塞(Blocked)
线程由于等待处理器外的其他条件无法运行,如条件变量的改变、加锁互斥量或I/O操作结束
终止(Terminated)
线程从起始函数中返回,或者调用pthread_exit,或者被取消,终止自己并完成所有资源清理工作。不是被分离,也不是被连接
        每个状态可能会细分成几个“子状态”。

二、线程的创建
        需要创建线程的方式有:    1、调用pthread_create
                                                   2、进程的信号通知机制设为SIGEV_THREAD
        注意:新线程可能在当前线程从phtread_create返回之前就运行了。甚至在pthread_create还没返回,新线程就已经执行完了。

        线程和初始进程的一些区别:
        1,参数不同
        2,函数返回结果不同:初始进程从main函数返回后,进程和进程内的所有线程都将终止;普通线程返回时,不影响进程内的其他线程。
             如果你希望在初始进程终止时,进程中的其他线程继续执行,则需要在初始进程中调用pthread_exit,而不是从main函数中返回。
        3,大多数系统中,初始线程运行在默认进程堆栈上,该堆栈可以增长到足够的尺寸;而在某些实现中,普通线程的堆栈空间是受限的,如果
            线程堆栈溢出,则程序会因段错误或总线错误而失败。

三、运行和阻塞
        线程阻塞的原因可能是:    1,试图加锁一个已经被锁住的互斥量
                                                   2,等待某个条件变量
                                                  3,调用Singwait等待尚未发生的信号
                                                  4,执行无法立即完成的I/O操作
                                                  5,线程还会由于如内存页错误之类的系统操作而被阻塞



四、终止
        单个线程可以通过3种方式退出,在不终止整个进程的情况下停止它的控制流:
        1,线程只是从启动例程中返回,返回值是线程的退出码。
        2,线程可以被同一进程中的其他线程取消。
        3,线程调用pthread_exit

头文件
#include <pthread.h>
函  数
void pthread_exit (void *rval_ptr)
        rval_ptr是一个无类型指针,进程中的其他线程可以通过调用pthread_join函数返回到这个指针。

头文件
#include <pthread.h>
函  数
int pthread_join (pthread_t thread, void **rval_ptr)
        线程将一直被阻塞,直到上面三种方法终止线程。 pthread_join 自动把连接的线程置于分离状态,这样资源
就可以恢复。如果线程已经处于分离状态,pthread_join 就会调用失败,返回EINVAL。

头文件
#include <pthread.h>
函  数
int pthread_detach( pthread_t  tid)
        使线程进入分离状态。线程在处于分离状态时,线程的底层存储资源可以在线程终止时立即被回收。在分离
状态下不能使用pthread_join等待线程的终止状态。

头文件
#include <pthread.h>
函  数
int pthread_cancel (pthread_t thread)
调用此函数来请求取消同一进程中的其他线程。线程可以选择忽略。

注意:如果线程只是从当前线程返回或者调用pthread_exit正常终止,rval_ptr将包含返回码。但是
如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED.

注意:pthread_create和pthread_exit函数的无类型指针参数能传递的数值可以不止一个,该指针可以传递包含更复杂信息
的结构的地址,但是注意这个结构所使用的内存在调用者完成 调用以后必须保证仍是有效的,否则就会出现非法内存访问。
例如,在调用线程(如main函数)的栈上分配了一个结构体,那么其他的线程在使用这个结构体时,结构体中的内容可能已经
改变了。
        又如,线程在自己的栈上分配了一个结构体,然后把它的指针传递给pthread_exit,那么当调用pthread_join的线程试图
再次使用该结构时,这个栈可能已经撤销,这块内存可能另作他用。
        解决的办法:可以使用全局结构体,不要放在main中。或者使用malloc函数分配结构体内存。

头文件
#include <pthread.h>
函  数
void pthread_cleanup_push (void (*rtn) (void *), void *arg)
函  数
void pthread_cleanup_pop (int execute)
        进程可以通过atexit函数安排进程退出时需要调用的函数,线程同样可以。这样的函数被称为“线程清理处理程序”。线程
可以建立多个清理处理程序。处理程序记录在栈中,这也意味着它们的执行顺序和它们的注册顺序是相反的。
        当线程执行以下动作时调用清理函数,调用参数为arg,清理函数rtn的调用顺序是pthread_cleanup_push函数来安排的。
        1,调用pthread_exit时
        2,响应取消请求时
        3,用非0 execute参数调用pthread_cleanup_pop时(如果execute参数设置为0时,清理函数将不被调用。但无论哪种情况,pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的清理处理函数)

注意:从线程自然的返回(如return)而终止线程的将不会调用线程清理函数。

进程原语和线程原语的比较
进程原语
线程原语
描述
fork
pthread_create
创建新的控制流
exit
pthread_exit
从现有的控制流中退出
waitpid
pthread_join
从控制流中得到退出状态
atexit
pthread_cleanup_push 
注册在退出控制流时要调用的函数
getpid
pthread_self
获取所在控制流的ID
abort
pthread_cancel
请求控制流的非正常退出
        
五、回收
        线程在终止后都要经过分离并且将资源回收。
        回收将释放所有在线程终止时未释放的系统和进程资源,包括保存线程返回值的内存空间、堆栈、保存
寄存器状态的内存空间等。在线程终止后上述资源就不该被访问了。
        终止线程将释放所有的系统资源,但你必须释放由该线程占有的程序资源。调用malloc或mmap分配的
内存可以在任何时候、由任何线程释放。互斥量、条件变量和信号灯可以由任何线程销毁,只要它们被解锁
并且没有线程等待。
        但是,只有互斥量的主人能够解锁它。如果线程终止时还有加锁的互斥量,则该互斥量就不能被再次使
用,因为不会被解锁。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux ,你可以通过不同的方法多次传递参数到线程函数。下面是一些常见的方法: 1. 使用结构体:创建一个结构体,将需要传递的参数封装在结构体,然后将结构体的地址作为参数传递给线程函数。线程函数可以通过解引用结构体指针来获取传递的参数。 ```c #include <stdio.h> #include <pthread.h> // 定义结构体 typedef struct { int value1; int value2; } ThreadArgs; // 线程函数,接收结构体参数 void* threadFunc(void* arg) { ThreadArgs* args = (ThreadArgs*)arg; int value1 = args->value1; int value2 = args->value2; printf("Thread function received values: %d, %d\n", value1, value2); // 线程操作... pthread_exit(NULL); } int main() { pthread_t tid; ThreadArgs args = { 42, 10 }; // 创建结构体并初始化参数 // 创建线程,并传递结构体参数 if (pthread_create(&tid, NULL, threadFunc, &args) != 0) { printf("Failed to create thread\n"); return 1; } // 等待线程结束 pthread_join(tid, NULL); return 0; } ``` 2. 使用数组:将需要传递的参数存储在数组,并将数组的地址作为参数传递给线程函数。线程函数可以通过访问数组元素来获取传递的参数。 ```c #include <stdio.h> #include <pthread.h> // 线程函数,接收数组参数 void* threadFunc(void* arg) { int* args = (int*)arg; int value1 = args[0]; int value2 = args[1]; printf("Thread function received values: %d, %d\n", value1, value2); // 线程操作... pthread_exit(NULL); } int main() { pthread_t tid; int args[] = { 42, 10 }; // 创建数组并初始化参数 // 创建线程,并传递数组参数 if (pthread_create(&tid, NULL, threadFunc, args) != 0) { printf("Failed to create thread\n"); return 1; } // 等待线程结束 pthread_join(tid, NULL); return 0; } ``` 无论使用结构体还是数组,都要确保传递的参数在线程函数正确地访问和使用。同时,需要注意传递参数的生命周期,确保参数在线程使用完毕之前保持有效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值