POSIX多线程—线程基本概念

作者:阿波
链接:http://blog.csdn.net/livelylittlefish/article/details/7957007

(整半年没有更新,发几篇以前的读书笔记。)

 

content

1. 线程建立与使用

  • 创建线程
  • 初始线程
  • 线程分离

2. 线程生命周期

  • 就绪态
  • 被阻塞
  • 线程终止
  • 线程回收

 

1. 线程建立与使用

 

创建线程

  • 通过pthread_create()函数创建线程;
    • 向该函数传递线程函数地址和线程函数参数;
    • 线程函数只有一个void*参数;
    • 该函数返回pthread_t类型的线程ID
  • 一般调用该函数创建线程,然后调用pthread_join()函数等待线程结束;
  • 在当前线程从函数pthread_create()中返回以及新线程被调度执行之间不存在同步关系;
    • 新线程可能在当前线程从pthread_create()返回值前就运行了;
    • 或在当前线程从pthread_create()返回之前,新线程就可能已经运行完毕了;

 

pthread_join()

  • 阻塞其调用者直到指定线程终止,然后可以选择地保存线程的返回值;
  • pthread_join()调用返回时,被连接线程就已经被分离(detached),再也不能连接该线程了;
  • 如果连接(joining)线程不关心返回值,或者它知道被连接(joined)的线程根本不返回任何值,则可向pthread_join()&retval参数传递NULL,此时,被连接线程的返回值将被忽略;

 

如何使用可参考pthread_create()pthread_join()手册;

 

初始线程

  • C程序运行时,首先运行main()函数,main()函数所在线程称为初始线程或主线程;
  • 初始线程可调用pthread_self()获得其ID,也可调用pthread_exit()来终止自己;
    • main()返回将导致进程终止,也将使进程内所用线程终止;
    • main()中调用pthread_exit(),这样进程就必须等待所有线程结束后才能终止;
  • 若初始线程将其ID保存在一个其他线程可以访问的空间,则其他线程就可以等待初始线程的终止或者分离初始线程;

 

线程分离

  • 分离一个正在运行的线程不会对线程带来任何影响,仅仅是通知系统当该线程结束时,其所属资源可以被回收;
  • 分离线程意味着通知系统不再需要此线程,允许系统将分配给它的资源回收;
  • 一个没有被分离的线程终止时会保留其虚拟内存,包括堆栈和其他系统资源。

 

2. 线程生命周期

 

在任意时刻,线程处于下表的四个基本状态之一。

 

状态

说明

就绪ready

线程能够运行,但在等待可用的处理器;

  • 可能刚刚启动
  • 或刚刚从阻塞中恢复
  • 或被其他线程抢占

运行running

线程正在运行;在多处理器系统中,可能有多个线程处于运行状态;

阻塞blocked

线程由于等待处理器外的其他条件无法运行,如条件变量的改变、加锁互斥量或IO操作结束;

终止terminated

不是被分离,也不是被连接,一旦线程被分离或者连接,它就可以被回收;

  • 线程从起始函数中返回
  • 或调用pthread_exit
  • 或被取消,终止自己并完成所有资源清理工作;

 

线程状态转换如下图。

说明

  • 线程开始处于就绪状态;
  • 当线程运行时,它调用特定的起始函数;
  • 它可能被其他线程抢占,或者因等待外来事情而阻塞自己;
  • 最终线程完成工作,或者从起始函数返回,或者调用pthread_exit函数,即进入终止状态;
  • 如果线程已被分离,则它立刻被回收重用;否则,线程停留在终止状态直到被分离或被连接;

 

就绪态

  • 线程刚被创建时;
  • 线程被解除阻塞再次可以运行时;
  • 运行线程被抢占时,如时间片到;

 

被阻塞

  • 试图加锁一个已经被锁住的互斥量;
  • 等待某个条件变量;
  • 调用singwait等待信号;
  • 执行无法立即完成的IO操作;
  • 内存页错误之类的系统操作;

 

初始线程(main()函数所在线程)与普通线程区别

  • 初始线程的启动函数main()是从程序外部调用的;如crt0.o文件复制初始化进程并调用main()函数;而普通线程的启动函数及其运行参数均由pthread_create()函数创建线程时传入,且由CPU调度的;
  • main()函数的参数是argcargv;普通线程的参数是void*,且由pthread_create()函数传入;
  • 若普通线程从启动函数中返回,则线程终止,而其他线程依然可以运行;但初始线程从main()返回时,进程终止,进程内所有线程也被终止;
    • 若希望在初始线程终止时,进程中的其他线程继续执行,则需要在初始线程调中调用pthread_exit()而非从main()返回;
  • 大多数系统,初始线程运行在默认进程堆栈上,该堆栈可以增长到足够尺寸;而某些实现中,普通线程的堆栈空间是受限的;
    • 如果线程堆栈溢出,则程序会出现段错误;

 

线程睡眠原因

  • 被阻塞,需要的某个资源不可用;
  • 被抢占,即系统将处理器分配给其他线程;

 

pthread_join()的详细解释

  • 用来等待一个线程的结束;
    • 是一个线程阻塞函数,调用它的函数将一直等待到被等待的线程结束为止;
    • 如,主线程调用pthread_join()等待它创建的线程运行结束,即主线程调用该函数后会被阻塞;
    • 当函数返回时,被等待的线程的资源被回收;
  • 若此时新线程没有运行,则它将在主线程被阻塞后从就绪态进入运行态;当新线程运行完毕并返回时,主线程才会被解除阻塞,返回就绪态;当处理器可用时,主线程或立即执行或等到创建的线程终止后重新运行直到结束;

 

线程终止

  • 一般地,线程从启动函数返回来终止自己;
  • 当调用pthread_exit()退出线程或者调用pthread_cancel()取消线程时,线程在调用每个清理过程后也进入终止状态;
  • 清理过程又线程通过pthread_cleanup_push()注册,且尚未通过pthread_cleanup_poo()删除;

 

Linux系统僵尸线程

  • 如果线程已经被分离,则会被回收;否则,线程处于终止状态,仍然可以被其他线程调用pthread_join()连接;
  • 这种线程被称为僵尸线程,像Unix系统中的进程已经结束但还没有被一个wait/waitpid调用回收一样,即使已经死了但还存在;
  • 僵尸线程可能会保留其运行时的大部分甚至所有资源,因此不应该让线程长时间处于这种状态;当创建不需要连接的线程时,应该使用detachstate属性建立线程使其自动分离;

 

线程回收

  • 如果使用detachstate属性(即设置属性为PTHREAD_CREATE_DETACH)建立线程,或者调用pthread_detach()分离线程,则当线程结束时将被立刻回收;
  • 如果终止线程没有被分离,则它将一直处于终止状态直到被分离(通过pthread_detach)或者被连接(通过pthread_join)
  • 线程一旦被分离,就不能再访问它;
  • 回收将释放所有在线程终止时未释放的系统和进程资源,包括
    • 保存线程返回值的内存空间、堆栈;
    • 保存寄存器状态的内存空间;
    • 实际上线程终止时上述资源就不能被访问了;
  • 一旦线程被回收,线程ID就无效了,不能再连接、取消或者执行其他任何操作;
    • 终止线程ID可能被分给新线程;

NPTL (Native POSIX Threads Library)

This is the modern Pthreads implementation.  By comparison with LinuxThreads, NPTL provides closer conformance  to  therequirements of the POSIX.1 specification and better performance when creating largenumbers of threads.  NPTL is available since glibc 2.3.2, and requires features that are present in theLinux 2.6 kernel.

Thread-safe functions

A thread-safe function is one that can be safely (i.e., it will deliver the same results regardless of whetherit is) called from multiplethreads at the same time.

Reference

# man pthreads

# man pthread_create

# man pthread_join

# man pthread_exit

# man pthread_cancel

你可能感兴趣的:(POSIX多线程)