线程包含了表示进程内执行环境必需的信息,其中包括进程中标示线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno变量以及线程私有数据。

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

线程标识:

进程ID在整个系统中是唯一的,但线程ID不同,线程ID只在它所属的进程环境中有效。进程ID的数据结构为pid_t,线程ID的数据结构为pthread_t。

比较两个线程ID是否相等:

#include <pthread.h>
int pthread_equal(pthread_t tid1,pthread_t tid2);//返回值:若相等返回非0值,否则返回0

线程可以通过调用pthread_self函数获取自身的线程ID:

#include <pthread.h>
pthread_t pthread_self(void); //返回值:调用线程的线程ID

线程的创建:

#include <pthread.h>
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,void *(*start_rtn)(void *),void * arg);//返回值:若成功则返回0,否则返回错误编号

当pthread_create成功返回时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于定制各种不同的线程属性。若设置为NULL,表示创建默认属性的线程。

新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数的arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。
     线程创建时并不能保证哪个线程会先运行:是新创建的线程还是调用线程。新创建的线程可以访问进程地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但是该线程的未决信号集被清除。
     每个线程都提供了errno的副本。

注意: pthread_create不是等待start_rtn函数运行完成后才返回的。一般情况下是pthread_create函数先返回,不过有时候start_rtn函数先运行完成后,pthread_create才返回。它们之间没有先后顺序。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>pthread_t ntid;void printids( const char *s)
{pid_t pid;pthread_t tid;pid = getpid();tid = pthread_self();printf("%s pid = %u pthread_t = %u\n",s,(unsigned int)pid, (unsigned int)tid);
}void* thr_fn(void * arg)
{printids("new pthread: ");return NULL;
}int main(int argc,char **argv)
{int err;err = pthread_create(&ntid,NULL,thr_fn,NULL);if ( err != 0 )perror("pthread_create");printids("main thread: ");sleep(1);exit(0);
}

运行结果:

huangcheng@ubuntu:~$ ./a.outnew pthread ID = 3077581680
main thread:  pid = 2458 pthread_t = 3077584576
new pthread:  pid = 2458 pthread_t = 3077581680

注意在本例里,主线程把新线程ID存放在ntid中,但是新建的线程并不能安全的使用它,如果新线程在主线程调用pthread_create返回之前就运行了,那么新线程看到的是未经初始化的ntid的内容,这个内容并不是正确的线程ID。

线程终止:

如果进程中的任一线程调用了exit、_Exit或者_exit,那么整个进程就会终止。与此类似,如果信号的默认动作是终止进程,那么把该信号发送到线程会终止整个进程。
     单个线程可以通过下列三种方式退出,在不终止整个进程的情况下停止它的控制流:

  • 线程只是从启动历程中返回,返回值是线程的退出码。
  • 线程可以被同一进程中的其他线程取消。
  • 线程调用pthread_exit.

注意:主线程调用pthread_exit也不会导致整个进程结束,其子线程还是继续运行。

#include <pthread.h>
void pthread_exit(void* rval_ptr);

rval_ptr是一个无类型的指针。进程中的其他线程可以通过调用pthread_join函数返回到这个指针。

#include <pthread.h>
int pthread_join(pthread_t thread,void **rval_ptr);//返回值:若成功则返回0 ,否则返回错误编号

调用线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。
     如果线程只是从它的启动例程返回,rval_ptr将包含返回码。如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED。
     可以通过调用pthread_join自动把线程置于分离状态,这样资源就可以恢复。如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。

线程可以通过调用pthread_cancel函数来请求取消同一进程中的其他线程:

#include <pthread.h>
int pthread_cancel(pthread_t tif); // 返回值:若成功则返回0,否则返回错误编号

在默认情况下,pthread_cancel函数会使得由tid标识的线程的行为表现为如同调用了参数为PTHREAD_CANCEL的pthread_exit函数,但是线程可以选择忽略取消方式或是控制取消方式。注意:pthread_cancel并不等待线程终止,它仅仅提出请求。

清理函数:

#include <pthread.h>
void pthread_cleanup_push(void (*rtn)(void*),void *arg);
void pthread_cleanup_pop(int execute);

线程可以安排它退出时需要调用的函数,这与进程可以用atexit函数安排进程退出需要调用的函数时类似的。这样的函数称为线程清理函数处理程序。线程可以建立多个清理处理程序。处理程序记录在栈中的,也就是说它们的执行顺序与它们注册时的顺序相反。

pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push() 的调用将在清理函数栈中形成一个函数链;从pthread_cleanup_push的调用点到pthread_cleanup_pop之间的程序段中的终止动作(包括调用pthread_exit()和异常终止,不包括return)都将执行pthread_cleanup_push()所指定的清理函数。
pthread_cleanup_push()函数执行压栈清理函数的操作,而pthread_cleanup_pop()函数执行从栈中删除清理函数的操作。

     注意pthread_cleanup_pop不管参数是零还是非零,都会从栈顶删除一个清理函数。
     在下面三种情况下,pthread_cleanup_push()压栈的“清理函数”会被调用,调用参数为arg,清理函数rtn的调用顺序是由pthread_cleanup_push函数来安排的。:

  • 线程调用pthread_exit()函数,而不是直接return.
  • 响应取消请求时,也就是有其它的线程对该线程调用pthread_cancel()函数。
  • 本线程调用pthread_cleanup_pop()函数,并且其参数非0.

如果execute参数置为0,清理函数将不被调用。无论哪种情况即不管pthread_cleanup_pop的参数时零还是非零,pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的清理处理程序,即删除清理函数链的栈顶。

pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:

#define pthread_cleanup_push(routine,arg) \
{
struct _pthread_cleanup_buffer _buffer; \
_pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute) \
_pthread_cleanup_pop (&_buffer, (execute)); \
}

可见pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。

注意:

  • pthread_exit终止线程与线程直接return终止线程的区别。
  • pthread_cleanup_push()函数与pthread_cleanup_pop()函数必须成对的出现在同一个函数中
示例代码:
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>void cleanup(void *arg)
{printf("cleanup: %s\n", (char*) arg);
}void *thr_fn1(void *arg)
{printf("thread 1 start\n");pthread_cleanup_push(cleanup, "thread 1-1  handler");pthread_cleanup_push(cleanup, "thread 1-2  handler");printf("thread 1 push complete\n");if (arg)pthread_exit((void*) 1);pthread_cleanup_pop(0);pthread_cleanup_pop(1);return((void*) 10);
}void *thr_fn2(void *arg)
{printf("thread 2 start\n");pthread_cleanup_push(cleanup, "thread 2-1  handler");pthread_cleanup_push(cleanup, "thread 2-2  handler");printf("thread 2 push complete\n");pthread_cleanup_pop(0);// if (arg)//         return ((void*) 2);pthread_cleanup_pop(1);//pthread_exit((void*) 20);return ((void*) 20);
}int main(void)
{int             err;pthread_t       tid1, tid2;void            *tret;err = pthread_create(&tid1, NULL, thr_fn1, (void*)1);if (err)fprintf(stderr, "can't create thread 1: %d\n", strerror(errno));        err = pthread_create(&tid2, NULL, thr_fn2, (void*)1);if (err)fprintf(stderr, "can't create thread 2: %d\n", strerror(errno));        err = pthread_join(tid1, &tret);if (err)fprintf(stderr, "can't join with thread 1: %d\n", strerror(errno));     printf("thread 1 exit code %lu\n", (unsigned long) tret);err = pthread_join(tid2, &tret);if (err)fprintf(stderr, "can't join with thread 2: %d\n", strerror(errno));     printf("thread 2 exit code %lu\n", (unsigned long) tret);exit(0);
}

运行结果:

huangcheng@ubuntu:~$ ./a.out
thread 2 start
thread 2 push complete
cleanup: thread 2-1  handler
thread 1 start
thread 1 push complete
cleanup: thread 1-2  handler
cleanup: thread 1-1  handler
thread 1 exit code 1
thread 2 exit code 20

在默认情况下,线程的终止状态会保存到对该线程调用pthread_join,如果线程已经处于分离状态,线程的底层存储资源可以在线程终止时立即被收回。当线程被分离时,并不能用pthread_join等待它的终止状态。对分离状态的线程调用pthread_join会失败,返回EINVAL。
pthread_detach调用可以用于使线程进入分离状态:
#include <pthread.h>
int pthread_detach(pthread_t tid);//返回值:若成功则返回0,否则返回错误编号

现在可以看出线程函数和进程函数之间的相似之处:

转载于:https://www.cnblogs.com/wangfengju/p/6172690.html

UNIX环境高级编程——线程相关推荐

  1. unix环境高级编程-线程(2)

    线程终止: 如果进程中的任意线程调用了exit._Exit或者_exit,那么整个进程就会终止,与此类似,如果默认的动作是终止进程,那么发送到线程的信号就会终止整个进程 单个进程可以通过三种方式退出, ...

  2. UNIX环境高级编程——线程同步之条件变量以及属性

    条件变量变量也是出自POSIX线程标准,另一种线程同步机制.主要用来等待某个条件的发生.可以用来同步同一进程中的各个线程.当然如果一个条件变量存放在多个进程共享的某个内存区中,那么还可以通过条件变量来 ...

  3. 【UNIX环境高级编程】线程同步

    当多个线程共享相同的内存时,需要确保每个线程看到一致的数据视图.如果每个线程使用的变量都是其他线程不会读取和修改的,那么就不存在一致性问题.同样,如果变量是只读的也不会有一致性问题.但是,当一个线程可 ...

  4. 《UNIX环境高级编程(第3版)》——1.7 出错处理

    本节书摘来自异步社区<UNIX环境高级编程(第3版)>一书中的第1章,第1.7节,作者:[美]W. Richard Stevens , Stephen A.Rago著,更多章节内容可以访问 ...

  5. unix环境高级编程 pdf_UNIX系统编程宝典,每一本都值得程序员珍藏

    这几本UNIX系统编程宝典,重印无数次,几代程序员都视如珍宝的几本书,小编在出版圈里快十年了,见证了这本书图灵版.异步社区版的出版.营销,对这套书倾注了一定的感情.今天继续分享给你们,好书总会有人还不 ...

  6. (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  7. UNIX环境高级编程笔记

    1.setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, len);   SO_REUSEADDR套接口选项允许为以下四个不同的目的提供服务:   ...

  8. 《Unix环境高级编程》学习笔记:从点到面

    以前在课堂上学习过<Unix初级教程(第四版)>,对于Unix有了一点了解.由于以后使用的需要,要对它进行比较深入的学习,为此需要阅读不少的书籍,这本<Unix环境高级编程>便 ...

  9. 《UNIX环境高级编程(第3版)》——2.6 选项

    本节书摘来自异步社区<UNIX环境高级编程(第3版)>一书中的第2章,第2.6节,作者:[美]W. Richard Stevens , Stephen A.Rago著,更多章节内容可以访问 ...

最新文章

  1. 对称加密和非对称加密
  2. 实战篇一 python常用模块和库介绍
  3. Python基于socket实现的TCP服务端
  4. MySQL添加中文记录报错解决方法
  5. 首次公开!阿里巴巴云原生实时数仓核心技术揭秘
  6. eslint airbnb 不允许尾随逗号
  7. docker run 服务名_在 WSL2.0 的 Ubuntu 18 里使用 Docker
  8. 2019-1-7Xiaomi Mi5 刷全球版MIUI教程
  9. angularjs之UI Grid 的刷新 本地数据源及HTTP数据源
  10. 桌面壁纸所放位置+魔镜壁纸的下载方法
  11. 多目标优化--MOEAD算法笔记
  12. 孙子兵法 三十六计(猫鼠版)
  13. 页面布局的方式有哪些?
  14. linux 访问西数网盘,西数不认盘,无法访问固件
  15. Win flex-bison 的简单使用
  16. 异质性分析:系数平滑可变模型
  17. 新kali版本,root默认密码
  18. 关于数据库中的schema的注释
  19. R语言添加Python模块错误的解决方法
  20. 西南财经大学本科毕业论文答辩PPT模板

热门文章

  1. 实战篇:如何建设企业的营销管理和分析平台
  2. bootstrap table 服务端分页
  3. Powershell 语法总结
  4. 安卓用targetSdk来兼容各个版本
  5. win8计算机用户名在哪里设置,windows8系统用户名微软ID和管理员账户概念详解
  6. 人脸识别sdk_开发实录:免费人脸识别SDK实现人证比对全过程
  7. visio中公式太小_五金冲压模具中的凹模有哪些注意事项,值得一看
  8. jquery实现app开发闹钟功能_一款让你真正摆脱懒觉的“闹钟APP软件”
  9. 初中数学抽象教学的案例_初中数学教学反思案例
  10. 京东app html源码_哔哩哔哩源码泄露,看不懂怎么办?