线程基础

线程标识

如同进程有进程ID一样,每一个线程也有一个线程ID,进程ID在整个系统中是唯一的,但线程ID只有在其所属进程上下文中才有意义。

线程ID是用pthread_t数据类型来表示。(Linux线程ID是用无符号长整型表示)

#include <pthread.h>

int pthread_self() 
    返回值:返回调用者线程ID

int pthread_equal(pthread_t tid1, pthread_t tid2)
    返回值:若相等,返回非0数值,否则,返回0
线程创建

线程创建时并不能确定是哪个线程会先运行,新创建的线程可以访问进程的地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但是该线程的挂起信号集会被清除

#include <pthread.h>

int pthread_create(pthread_t *restrict tidp,
                   const pthread_attr_t *restrict_attr,  
                   void* (*start_rtn)(void *),
                   void *restrict_arg)

    返回值:成功返回0,出错返回错误编号

传统的UNIX模型中,每个进程只有一个控制线程。程序开始运行时,它是以单个进程中的单个控制线程启动。

新创建的线程ID会被设置为tidp指向的内存单元,attr用于定制各种线程,新创建的线程从start_rtn开始运行,arg用于给start_rtn传参数。

pthread函数在调用失败时通常返回错误码,它并不像其他函数一样设置errno的值。

代码示例:

#include <pthread.h>
#include <stdio.h>

void* thr_fn(void *arg)
{
    char **str = (char **)arg;
    printf("thread ID: %lu\n", pthread_self());
    for (; *str != NULL; str++)
        printf("%s\n", *str);
    return NULL;
}

int main()
{
    int err;
    char *args[3] = {"hello", "world", NULL};
    pthread_t tid; 

    err = pthread_create(&tid, NULL, thr_fn, (void *)args);
    sleep(1);      
}

这里有1个特别的地方需要注意,主线程那里sleep,是因为没有sleep,那么新线程可能还没有开始运行,主线程就已经结束退出。

线程终止

如果进程中的任意线程调用exit_Exit_exit那么整个进程就会终止。同时发送到线程的信号也有可能终止掉进程。
单个线程可以通过以下3种方式退出:

  • 线程可以简单地从启动例程中返回,返回值是线程退出码
  • 线程可以被同一进程中其他线程取消
  • 线程调用pthread_exit()
#include <pthread.h>

void pthread_exit(void *rval_ptr);

rval_ptr指针会记录线程退出码。

回收已终止线程资源
#include <pthread.h>

void pthread_join(pthread_t thread, void **rval_ptr);
    
    返回值:成功返回0,出错返回错误编号

调用线程将一直阻塞,直到指定的线程调用pthread_exit,从启动例程返回或者被取消。如果线程简单的从它的启动例程返回,那么rval_ptr就包含返回码,如果线程被取消,由rval_ptr指定的内存单元就设置为PTHREAD_CANCELED

  • 代码示例:
  #include <stdio.h>
  #include <pthread.h>

  void* thr_fn1()
  {
      printf("thread id: %lu\n", pthread_self());
      printf("world\n");
      return (void *)0;
  }

  void* thr_fn2()
  {

      printf("thread id: %lu\n", pthread_self());
      printf("world\n");
      pthread_exit((void *)2);
  }

  int main()
  {
      pthread_t tid1, tid2;
      void* tret;

      pthread_create(&tid1, NULL, thr_fn1, NULL);
      pthread_create(&tid2, NULL, thr_fn2, NULL);

      pthread_join(tid1, &tret);
      printf("thread1 exit code: %d\n", (int)tret);

      pthread_join(tid2, &tret);
      printf("thread2 exit code: %d\n", (int)tret);
  }
分离线程

线程是可结合(joinable)或者是分离(detached)的。如果对一个对一个分离的线程调用pthread_join,调用会失败,返回EINVAL

一个可结合的线程能够被其他线程收回资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是没有被释放。

但是一个分离的线程是不能被其他线程回收或者杀死,它的存储器资源在它终止时由系统自动回收。

#include <pthread.h>

int pthread_detach(pthread_t tid)

    返回值:成功返回0,出错返回错误编号

参考资料
[1]《UNIX环境高级编程》[美] W. Richard Stevens Stephen A. Rago
[2] 《深入理解计算机系统》[美] Randal E. Bryant David R. O'Hallaron
[3] 《现代操作系统》[荷] Andrew S. Tanenbaum

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 10.1 引言 在前面的章节中讨论了进程,学习了UNIX进程的环境、进程间的 关系以及控制进程的不同方式。可以看到...
    MachinePlay阅读 500评论 0 0
  • 线程 线程的概念 典型的UNIX进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事。有了多个控制线程后,在...
    ColdWave阅读 1,505评论 0 0
  • Linux系统下的多线程遵循POSIX线程接口,称为 pthread。编写Linux下的多线程程序,需要使用头文件...
    呼啦啦的爱阅读 667评论 0 1
  • 线程介绍 概念 一个进程可以包含多个线程,统一程序中的所有数据均会独立地执行相同的程序,并且共享一份全局内存区域,...
    永远的纸条阅读 166评论 0 0
  • 原型: #include int pthread_eaual(pthread_t tid1,p...
    不合格码农阅读 125评论 0 0