POSIX多线程程序设计(三)线程的状态及简单使用

POSIX多线程程序设计(三)

>>如何选择使用线程或不使用线程

  • 本身是非并发的问题,如果使用了多线程只会降低程序的性能并使用程序复杂。

例如:
如果程序中的每一步都需要上一步的结果,则使用多线程不会有任何帮助,因为每个线程都不得不等待其它线程的结束。

最适合使用线程的应用:
1. 计算密集型应用
2. I/O密集型应用

基本的线程同步模型为:使用互斥量保护共享数据,使用条件变量来通信,还可以使用其它的同步机制,如信号量、管道、消息队列。

  • 互斥量 :允许线程在访问共享数据时锁定它,以避免其它线程的干扰。
  • 条件变量:允许线程等待共享数据到达某个期望的状态(例如队列为空或资源可用)。

>>线程的一些特性

  • | 开始运行的主线程-初始线程(main)是特殊的,它的特殊性在于main函数返回时会导致所在进程终止。即,进程结束时不会等待其它线程的结束,当进程结束时,所有线程、状态和它们的工作结果都会被无理由的全部清理掉。所以要确保main函数不会早于线程结束。
  1. 比较两个线程的ID的大小是无意义的,因为线程ID之间不存在顺序。如果两个线程ID表示的是同一个线程,则pthread_equal函数返回非0值,否则返回0值。
  2. pthread_self函数用来获取自身线程ID
  3. pthread_eixt函数用来终止自身线程
  4. 其它线程如果获取一个线程的ID,则可以waitdetach这个线程
  5. 分离一个正在运行的线程不会对线程带来任何影响,它仅仅是通知系统当该线程结束时,其所属资源可以被回收。一个没有被分离的线程终止时会保留其虚拟内存,包括它们的堆栈和其它系统资源。
  6. 创建一个线程pthread_create
  7. 如果要创建一个从不需要控制的线程, 可以使用属性(attribute)来建立线程
  8. 如果一个线程已经创建好了,但不想等待以后也不想控制它,则可以用pthread_detach函数来分离它
  9. 一个创建好的线程,也可以自己分离自己,任何其它的线程如果获取它的ID也能分离它
  10. 如果需要获取线程的返回值,或者需要获知其线程何时结束,则可以调用pthread_join,这个函数会阻塞调用者,直到指定的线程结束,并得到其返回值。
  11. 一个进程内的所有线程总是共享一个地址空间和文件描述符。共享地址空间使得线程间能够高效的通信。

>>一个简单的线程函数使用

首先我们先写一个简单的错误头文件,方便以后使用

errors.h

#ifdef DEBUG
# define DPRINTF(arg) printf arg
#else
# define DPRINTF(arg)
#endif

#define err_abort(code, text) do{ \
    fprintf(stderr, "%s at \"%s\":%d: %s\n",\ 
text, __FILE__, __LINE__, strerror(code)); \
abort(); \
    } while (0);

#define errno_abort(text) do{\
        fprintf(stderr, "%s at \"%s\":%d: %s\n", \
            text, __FILE__, __LINE__, strerror(errno));\
        abort();\
        }while (0);

#endif
然后我们写一个简单的线程

main.cpp

#include 
#include "errors.h"

// 线程函数
void * thread_routine(void* arg)
{
    int value = *(int*)arg;
    delete (int*)arg;
    printf("thread_routine is called: %d\n", value);

    return arg;
}

int main()
{
    pthread_t thread_id;
    void *thread_result = (void*)100;
    int status;

    // 第4个参数用以传递给线程函数
    int* arg = new int;
    *arg = 123;
    // 创建一个线程
    status = pthread_create(&thread_id, NULL, thread_routine, (void*)arg);
    if (status != 0)
        err_abort(status, "Create thread");

    // join会阻塞主线程,直到子线程运行结束
    // 当join返回时,被join的线程就已经被detach,再也不能join了
    // 如果不关心返回值,则第二个参数可以置NULL
    status = pthread_join(thread_id, &thread_result);
    if (status != 0)
        err_abort(status, "Join thread");

    if (thread_result == NULL)
        printf("thread_result is NULL\n");
    else 
        printf("thread_result is not NULL\n");

    return 0;
}

结果输出

thread_routine is called: 123
thread_result is not NULL

>>线程的四种状态

状态 含义
就绪(ready) 线程能够运行,但在等待可用的处理器,可能刚刚启动,或刚刚从阻塞中恢复,或者被其它线程抢占
运行(running) 线程正在运行。在多处理器系统中,可以有多个线程处于运行状态
阻塞(blocked) 线程由于等待处理器外的其他条件无法运行,如条件变量的改变、加锁互斥量或I/O操作结束
终止(terminated) 线程从起始函数中返回,或调用pthread_exit,或者被取消,终止自己并完成所有资源清理工作。一旦线程被分离或连接,它就可以被收回

POSIX多线程程序设计(三)线程的状态及简单使用_第1张图片

>>线程的创建

  1. 线程创建的最主要方式就是调用pthread_create
  2. 如果进程的信号通知机制设为SIGEV_THREAD,则当进程收到一个POSIX信号时也会创建线程

新的线程被创建后,是处于就绪状态
注意:受调试机制的限制,新的线程可能在就绪状态停留一段时间后才开始执行。但当前线程从函数pthread_create中返回及新线程被调试执行之间不存在同步关系。即,新线程可能在当前线程从pthread_create返回之前就运行了,甚至在其返回之前,新线程就已经运行完毕了。

>>线程的启动

线程被创建时,指定的线程函数(及其参数)就执行了

>>线程的运行状态

大多数线程会不时的睡眠,线程之所以会睡眠是因为它需要的某个资源不可用(即被阻塞),或者因为系统将处理器分配给其它线程(即抢占)。

  • 就绪
    1. 线程刚被创建时,或刚被解除阻塞时,线程处于就绪状态就绪意味着线程在等待可用的处理器。
    2. 一个正在运行的线程被抢占时,如时间片机制抢占(因为它已经运行足够长的时间了),线程也立即进入就绪状态
  • 运行
    1. 当处理器选中一个就绪线程执行它时,该线程进入运行状态。通常这意味着可能某个其它的线程被阻塞或者被时间片机制抢占,则处理器会保存被阻塞(或被抢占)线程的环境并恢复一个就绪线程的环境。
    2. 注意:在多处理器系统中,一个未用的处理器可以执行一个就绪线程而不必阻塞其它正在运行的线程。
  • 阻塞
    当一个正在运行的线程,遇到如下情况会阻塞
    1. 试图加锁一个已经锁住的互斥量;
    2. 等待某个条件变量;
    3. 调用singwait等待尚未发生的信号;
    4. 执行无法立即完成的 I/O 操作;
    5. 错误,如内存页错误之类的系统操作;
      在线程等到某个事件发生后,就会立即从阻塞状态重新进入就绪状态。如果处理器可用,它可以马上进入到运行状态
  • 终止
    1. 线程启动函数运行结束返回就会终止线程
    2. 调用 pthread_eixt 退出线程 或 调用 pthread_cancel 取消线程,它们会触发清理函数后再进入终止状态。
      • 清理函数注册:pthread_cleanup_push
      • 清理函数删除:pthread_cleanup_pop
      • 线程的非空私有数据:destructor 函数
    3. 线程终止时至少保留了线程ID,void*返回值,该返回值从线程启动函数中返回或调用pthread_exit时设定。
      pthread_exit 和 pthread_cancel 的区别:
      取消线程的返回值总是 PTHREAD_CANCELLED

僵线程
如果一个线程已经被分离,则它会立即进入回收,否则,线程处于终止状态,它还可以被其它线程调用pthread_join 连接。这种线程被称为僵线程,因为它虽然已经死了但还存在。僵线程可能会保留其运行时的大部分甚至所有资源。因此,不应该让线程长时间处于这种状态,当创建不需要连接的线程时,应该使用 detachstate 属性建立线程使其自动分离。

pthread_join是有用的,但不是必要的
当你需要从线程中获取返回值时,一般情况下肯定是要建立分离的线程,这与 pthread_join 相 矛盾。
可以不使用join,方法如下:
传递一个某种形式的数据结构给某个工作线程,且使该数据结构能够被其它线程访问,你可以在工作线程中改变数据结构后,广播某个条件变量。做完这些,这个工作线程就可以被立即回收了,而你关心的返回值或数据始终保存在你随时可以找到的地方。

注意点:当你所关心的线程终止了,则它里面所有资源就不该再被访问了

你可能感兴趣的:(Linux)