线程---中

来源:互联网 发布:mysql 主键查询优化 编辑:程序博客网 时间:2024/06/02 09:10

多线程编程

模块一:线程的基本概念
- 线程的概念
进程是在进程地址空间运行的执行流

2.线程与进程的区别

  • 线程是系统分配资源的实体,是系统分配资源的基本单位;线程是基本单位;
  • 在linux下,没有真正意义上的线程,只有进程;
  • 线程是在进程地址空间内运行的执行流;
  • 线程id(用函数pthread_self()得到)
  • 上下文信息,包括各种寄存器的值、程序计数器和栈指针
  • 栈空间
  • error变量
  • 信号屏蔽字
  • 调度优先级

    3.vfork函数创建子进程的特点(两点):

  • vfork函数创建新进程,而该新进程的目的是exec一个新程序,它不会将父进程的地址空间拷贝给子进程,在该新进程调用exit或exec之前,它在父进程的地址空间类运行

  • vfork创建的子进程保证子进程先运行,父进程再运行

4.线程库函数是由POSIX标准定义,成为POSIX thread或pthread,在linux上线程函数位于libpthread共享库中,因此在编译时要加上-lpthread

模块二:线程的控制
线程的控制包括线程的创建、等待和终止
一、线程的创建
函数:
这里写图片描述

  • 参数一pthread_t * pthread:是一个输入型参数,先定义一个pthread_t id,将&id传入
  • 参数二const pthread_attr_t *arr:arr是一个结构体指针,用来设置新线程的属性,一般为NULL(默认其原有属性)
  • 参数三void* (start routine)(void):它是一个函数指针,执行进程分配的函数
    返回值:成功返回0,失败返回出错码(因为是错误码而不是错误信息,所以不能用perror函数)

二、线程等待
函数:
这里写图片描述

  • pthread_t thread:线程id
  • void** retval:该参数是一个void**型的变量,是一个地址变量,表示pthead-run函数的返回值的地址,若不关心可以设置为NULL,若想输出pthread_run函数的退出码,则定义一个变量void* ret,该参数为&ret,是一个输出型参数,打印退出码即(int)ret;
    返回值:成功返回0,失败返回错误码

调用pthread_join得到pthread_run的返回值,得到子进程的退出状态

thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,如下:

  1. pthread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值,例return((void*)123)
  2. 如果thread线程是被别的线程调用pthread_cancel异常取消掉,value_ptr所指向单元里存放的是常数PTHRAD_CANCELED(这是一个宏,(void*)-1)
  3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数,如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数,例如:pthread_exit((void*)123);

    进程的取消
    函数:
    这里写图片描述
    尽量不要让线程自己取消自己

将错误码转换为错误信息
函数:
这里写图片描述
参数errnum:表示错误码
代码验证
这里写图片描述
部分错误码显示
这里写图片描述

验证线程pthread的三种退出方式的退出码
这里写图片描述这里写图片描述

模块三:有关分离线程

1.线程的属性
线程是可分离和可结合的

  • 一般我们在创建线程时,默认线程属性是可结合的,每个可结合的线程都需要调用pthread_join显示回收,否则会造成内存泄漏;
  • 线程的属性可以设置为可分离的,不需要pthread_join显示回收,操作系统自动回收,该线程不能被其他线程杀死(在该线程调用pthread_detach函数设置为可分离的以后,不能在主线程中对该线程pthread_join,否则会出错)
  • 如果一个可结合的线程结束运行但没有被pthread_join,则该线程的状态类似于进程的僵死状态

函数:
这里写图片描述
由于调用pthread_join后(等待子线程运行完毕),如果该线程没有结束,调用者会被阻塞。有些时候主线程并不希望调用pthread_join而阻塞,还要处理之后到来的请求,可在子线程调用pthread_detach(pthread_self())函数取消该线程或则在主线程中调用pthread_detach(id),取消该线程

模块四:线程的互斥与同步

由于线程间共享进程的资源,并且没有任何的保护机制,所以线程在访问临界资源时,由于没有任何的保护机制,所以可能线程在访问临街资源时发生发生线程间的切换,导致出错
例如:在对进程内的一个全局变量++,对于操作系统看来,这个算式共分为三步:

  1. 将全局变量从存储器读到CPU中
  2. 对全局变量做++
  3. 将数据写回到存储器

    在这个过程中,各个线程有可能在任意时刻发生切换,发生错误
    如下,都全局变量gcount累加50000次
    这里写图片描述
    结果图:
    这里写图片描述

与我们猜想不同的原因:可能在这过程每次A线程执行完一次或多次累加才执行线程B的累加
为了增加出错率,我们要增加进程的切换,什么情况下最容易产生线程的切换?
当执行流由内核态返回到用户态时容产生进程的切换
内核态:执行操作系统的代码
用户态:执行用户自己的代码

并且我们再定义一个全局变量,将 gcount赋值给它,让累加一次不再是三步而是多步,增加切换的几率
验证代码如下:
这里写图片描述
结果图:
这里写图片描述
不是100000次,验证了我们的想法

接下来,我们引入互斥锁的概念来解决这个问题
这里写图片描述
三个函数分别是“加锁”“尝试加锁”“解锁”
使用如下图:
这里写图片描述
这里写图片描述
结果图:
这里写图片描述

当我们多创建几个线程,则交互次数多,发生错误的几率也就越大,如下:
这里写图片描述
结果图:
这里写图片描述

0 0
原创粉丝点击