线程(一)创建和终止



一 线程基本概念:

(一)注意

1.线程在进程内执行环境必需的信息:线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno变量以及线程私有数据。

*一组并发线程运行在一个进程的上下文中,每个线程都有自己独立的线程上下文:线程ID、栈、栈指针、程序计数器、条件码和通用目的的寄存器值。每个线程和和其它线程共享进程上下文的剩余部分:整个用户虚拟地址空间(代码、读/写数据、堆、已经所有共享库代码和数据区),也共享同样的打开文件集合。


2.进程内的所有信息对该进程内的所有线程都是共享的:包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。


3.Pthreads引入了一种全新的报错方式:Pthreads中的函数通过返回值表示错误状态,而没有使用errno变量。

//出错不会导致程序终止,所以我们需检测每一个函数的返回值(自己领悟)。

Pthreads在提供了一个在线程内的errno变量,只是为了和使用errno的现有函数兼容。每个线程都有属于自己的局部errno以避免一个线程干扰另一个线程。


4.函数中的无类型指针参数(void *ptr)能可以传递多个值,可以用该指针指向一个结构体。


(二)线程标识:

(1)线程ID:

*线程ID只在它所属的进程环境中有效和唯一,进程ID在整个系统唯一。

*线程ID的类型为pthread_t,可移植的操作系统实现不能把它作为整数。

(2)相关函数:

*pthread_self():获取线程ID,返回调用线程的线程ID。

*pthread_equal(pthread_t tid1,pthread_t tid2):比较两个线程ID是否一样。


(三)线程创建:

1.每个进程只有一个控制线程,程序刚开始运行时,是以单进程中的单个控制线程启动的,在创建多个控制线程之前,程序的行为与传统的进程没有什么区别。

2.通过pthread_create创建线程:

*函数原型,编译和链接时需-lpthread:

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

*tid:tid指向的内存单元被设置为新创建线程的线程ID。

*attr:用来定制各种不同的线程属性。(不定制时,设置为NULL)

*start_routine:新创建的线程从start_routine函数的地址开始运行,还函数只有一个无类型指针参数arg。

*成功时返回0;出错时返回一个错误代码,并且*tid未被定义。

*errors:

EAGAIN:Insufficient resources to create another thread,or a system-imposed limit on the number of threads was encountered.

EINVAL:Invalid settings in attr.

EPERM:No permission to set the scheduling policy and parameters specified in attr. 

*通常写法:

if( ( err=pthread_create(...) ) !=0)//不设置errno变量。

{调用strerror(err)返回错误代码的信息。线程没有perror函数可用}


3.线程创建时不能保证哪个线程会先执行。

4.linux中使用clone系统调用来实现pthread_create。

*clone系统调用创建子进程,这个子进程可以共享父进程一定数量的执行环境,这个数量是可配置的。


(四)线程终止:

1.单个线程,在不终止整个进程的情况下终止的方式:

*线程从启动例程返回,返回值是线程的退出码。

*线程可以被同一进程的其他线程取消(pthread_cancel)。

*线程调用pthread_exit。

* 主线程从main函数返回(或者return)时会终止子线程,若主线程调用pthread_exit()终止的话则不会终止子线程。

注意:如果进程中任意函数调用exit、_exit或_Exit,那么整个进程就会终止。


2.相关函数:

*pthread_exit:

void pthread_exit(void *retval):终止调用线程,并返回retval作为线程的返回值,其他同进程中的线程可以用pthread_join函数访问到这个指针。如果对线程返回值不感兴趣,则可以把retval设为NULL,这样pthread_join只等待指定线程终止,不获取线程的终止状态。


*pthread_join:

int pthread_join(pthread_t thread,void **retval):调用线程一直阻塞,等待thread指定的线程终止,thread指定的线程必须是joinable的。


*pthread_detach(使线程进入分离状态):

原型:

int pthread_detach(pthread_t tid):使线程进入分离状态。

默认情况下,线程的终止状态会保存到对该线程调用pthread_join,如果线程已经处于分离状态,线程的底层存储资源可以在线程终止时就立即释放。

当线程是分离状态时,不能使用pthread_join等待它的终止状态,若调用返回错误(EINVAL)。


*pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate):设置线程属性detachstate。

detachstate:PTHREAD_CTREATE_DETACH(分离状态启动)或PTHREAD_CREATE_JOINABLE(正常状态启动)。

*pthread_attr_getdetachstate(const pthread_attr_t *attr,int *detachstate):获取当前的线程属性detachstate。



*pthread_cancel:

线程可以通过pthread_cancel来请求取消同一进程中的其他线程。

*int pthread_cancel(pthread_t tid):发送一个取消请求给线程tid,是否或何时目标线程对取消请求响应取决于线程的两个属性:可取消状态和可取消类型。

*pthread_cancel不等待线程终止,在默认情况下,线程在取消请求发出以后继续运行,直到线程到达某个取消点。取消点:线程检查是否被取消并按照请求进行动作的一个位置。某些函数在调用时,取消点都会出现,也可以调用pthread_testcancel自己添加取消点。



*线程清理处理程序:

线程可以建立多个处理程序,处理程序记录在栈中,所以执行顺序与注册顺序相反。

void pthread_cleanup_push(void (*rtn)(void *),void *arg)//arg为清理函数的参数。

void pthread_cleanup_pop(int execute)

线程执行以下动作时,会调用清理函数:

~调用pthread_exit。

~响应取消请求。

~用非0参数执行pthread_cleanup_pop。


注意:

*如果参数execute为0,pthread_cleanup_pop只删除上一个清理函数,而不执行。

*线程从启动例程返回而终止的话,不调用线程清理处理程序。

*pthread_cleanup_push与pthread_cleanup_pop必须成对出现,否则编译不通过。

*pthread_cleanup_push注册一个回调函数,如果你的线程在对应的pthread_cleanup_pop之前异常退出(return是正常退出,其他是异常),那么系统就会执行这个回调函数(回调函数要做什么你自己决定)。但是如果在pthread_cleanup_pop之前没有异常退出,pthread_cleanup_pop就把对应的回调函数取消了。



二 线程执行模型:

(一)多线程执行模型在某些方面与多进程的执行模型是相似的。每个进程开始生命周期时都是单一线程,这个线程称为主线程。

(二)多线程执行与多进程执行的不同:

1.线程的上下文要比进程上下文小得多,所以线程的上下文切换要比进程的上下文切换快的多。

2.线程不像进程那样,不是按照严格的父子层次来组织。和一个进程相关的线程组成一个对等(线程)池。主线程和其它线程的区别仅在于主线程是进程中第一个运行的线程。对等(线程)池的主要影响是:一个线程可以杀死它的任意对等线程,或者等待它的任意对等线程终止,每个对等线程可以读写相同的共享数据。













  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线程创建终止方法可以使用Java中的Thread类来实现。 ### 线程创建方法 1. 继承Thread类并重写run()方法,然后调用start()方法启动线程。 ```java public class MyThread extends Thread { @Override public void run() { System.out.println("Hello, I am a new thread!"); } } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } ``` 2. 实现Runnable接口并重写run()方法,然后创建Thread对象并调用start()方法启动线程。 ```java public class MyRunnable implements Runnable { @Override public void run() { System.out.println("Hello, I am a new thread!"); } } public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); } ``` ### 线程终止方法 1. 使用标志位终止线程。在run()方法中通过判断标志位来决定是否终止线程。 ```java public class MyThread extends Thread { private volatile boolean flag = true; public void stopThread() { flag = false; } @Override public void run() { while (flag) { // do something } } } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // 等待一段时间后终止线程 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } thread.stopThread(); } ``` 2. 使用interrupt()方法终止线程。在run()方法中通过判断线程是否被中断来决定是否终止线程。 ```java public class MyThread extends Thread { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { // do something } } } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // 等待一段时间后中断线程 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值