本章中,主要是介绍控制线程行为方面的内容,同时介绍了在同一进程中的多个线程之间如何保持数据的私有性以及基于进程的系统调用如何与线程进行交互.

一.线程属性

我们在创建线程的时候可以通过修改pthread_attr_t结构的值来修改线程的属性,将这些属性与创建的线程联系起来。调用pthread_attr_init以后,pthread_attr_t结构所包含的内容就是操作系统实现支持的线程所有属性.

#include <pthread.h>

int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
//两者的返回值都是:若成功则返回0,否则返回错误编号

如果在创建线程时就知道不需要了解线程的终止状态,则可以修改pthread_attr_t结构中的detachstate属性,让线程以分离状态启动。

int pthread_attr_getdetachstate(const pthread_attr_t *attr,int* detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);
//若成功则返回0,否则返回错误编号

可以调用pthread_attr_getdetachstate函数获取当前线程的detachstate线程属性,第二个参数所指向的整数可以被设置为PTHREAD_CREATE_DETACHED,以分离状态启动。也可以设置为PTHREAD_CREATE_JOINABLE,正常启动线程,应用程序可以获取线程的终止状态。

//以分离状态创建线程
int makethread(void* (*fn)(void*),void* arg){
int err; pthread_t tid;
pthread_attr_t attr; err=pthread_attr_init(&attr);
if(err!=){
return err;
}
err=pthread_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
if(err==){
err=pthread_create(&tid,&attr,fn,arg);
}
pthread_attr_destroy(&attr);
return err;
}

POSIX.1定义了线程栈属性的一些操作接口,线程栈属性的查询和修改一般是通过新的函数pthread_attr_getstack和pthread_attr_setstack来进行.

int pthread_attr_getstack(const pthread_attr_t *attr,void ** stackaddr,
size_t *stacksize);
int pthread_attr_setstack(pthread_attr_t *attr,void *stackaddr,size_t *stacksize);
//两者的返回值都是:若成功则返回0,否则返回错误编号
int pthread_attr_getstacksize(const pthread_attr_t *attr,size_t *stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr,size_t stacksize);
//若成功则返回0,否则返回错误编号 线程属性guardsize控制着线程末尾之后用以避免栈溢出的扩展内存的大小、
int pthread_attr_getguardsize(const pthread_attr_t* attr,size_t guardsize);
int pthread_attr_setguardsize(pthread_attr_t* attr,size_t guardsize); 并发控制着用户级线程可以映射的内核线程或进程的数目
int pthread_getconcurrency(void); /返回值:当前的并发度
int pthread_setconcurrency(int level);

二.同步属性

线程的同步对象也有属性,主要讨论的是互斥量、读写锁和条件变量的属性.

1.互斥量的属性

#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t* attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t* attr);
//成功则返回0,否则返回错误编号

如果进程共享互斥量设置为PTHREAD_PROCESS_SHARED,从多个进程共享的内存区域中分配的互斥量可以用于这些进程的同步.

可以使用pthread_mutexattr_getshared函数查询pthread_mutexattr)t结构,得到它的进程共享属性,可以用pthread_mutexattr_setpshared函数修改进程共享的属性。

int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,int pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,int pshared);
//成功则返回0,否则返回错误编号

进程共享互斥量属性设置为PTHREAD_PROCESS_PRIVATE时,允许pthread线程库提供更加有效的互斥量实现。

类型互斥量的属性:

PTHREAD_MUTEX_NORMAL类型是标准的互斥量类型,并不做任何特殊的错误检测或死锁检测。PTHREAD_MUTEX_ERRORCHECK互斥量类型提供错误检测。

PTHREAD_MUTEX_RECURSIVE互斥量类型允许同一线程在互斥量解锁之前对互斥量进行多次加锁,用一个递归互斥量维护锁的计数,在加锁和解锁次数不相同的情况下不会释放锁。

PTHREAD_MUTEX_DEFAULT类型可以用于请求默认语义。操作系统在实现它的时候可以把这种类型自由地映射到其他类型。

用来获取互斥量类型
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr,int *type);
int pthread_mutexattr_settype(pthread_mutexattr_t *attr,int type);

2.读写锁属性

int pthread_rwlockattr_init(pthread_rwlockattr_t* attr);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t* attr); int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* attr);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t* attr);

3.条件变量属性

int pthread_condattr_init(pthread_condattr_t* attr);
int pthread_condattr_destroy(pthread_condattr_t* attr); int pthread_condattr_getpshared(const pthread_condattr_t* attr,int * pshared);
int pthread_condattr_setpshared(pthread_condattr_t* attr,int pshared);

三.重入与线程私有数据

如果一个函数在同一时刻可以被多个线程安全地调用,我们就称为这个函数是线程安全的.很多函数它们并不是线程安全的,因为它们返回的数据时存放在静态的内存缓存区内,通   过修改接口,要求调用者自己提供缓存区可以使线程变为线程安全的.例如:acstime_r、gmtime_r等,在名字最后加了_r,以表明这个函数是可重入的 。

线程私有数据时存储和查询与某个线程相关的数据俄一种机制,希望每个线程可以独立地访问数据副本,而不需要担心与其他线程的同步访问问题。

在分配线程私有数据之前,需要创建与该数据关联的键。这个键用于获取对线程私有数据的访问权。使用pthread_key_create创建一个键.

int pthread_key_create(pthread_key_t* keyp,void (*destructor)(void*));

int pthread_key_delete(pthread_key_t* key);

创建的键存放在keyp指向的内存单元,这个键可以被进程中的所有线程使用,但每个线程把这个键与不同的线程私有数据地址进行关联,创建线程时,每个线程的数据地址设为null值。pthread_key_create可以选择关联该键的析构函数,当线程采用pthread_exit或者线程执行返回,正常退出时,析构函数就会被调用。但如果调用exit、_exit、_Exit、abort或出现其他非正常的退出时,就不会调用这个析构函数。

pthread_once_t initflag=PTHREA_ONCE_INIT;
int pthread_once(pthread_once_t *initflag,void (*initfn)(void));

initflag必须是一个非本地变量(即全局变量或静态变量),而且必须初始化为PTHREAD_ONCE_INIT.每个线程都调用pthread_once,系统能保证初始化例程initflag只被调用一次,即在系统首次调用pthread_once时。

键一旦创建,就可以通过调用pthread_setspecific函数把键和线程私有数据关联起来,可以通过pthread_getspecific函数获得线程私有数据的地址。

#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h> static pthread_key_t key;
static pthread_once_t init_done=PTHREAD_ONCE_INIT;
pthread_mutex_t env_mutex=PTHREAD_MUTEX_INITIALIZER; extern char** environ; static void thread_init(void){
pthread_key_create(&key,free);
} char* genenv(const char* name){
int i,len;
char* envbuf; pthread_once(&init_done,thread_init);
pthread_mutex_lock(&env_mutex); envbuf=(char*)pthread_getspecific(key);
if(envbuf==NULL){
envbuf=(char*)malloc(ARG_MAX);
if(envbuf==NULL){
pthread_mutex_unlock(&env_mutex);
return NULL;
}
pthread_setspecific(key,envbuf);
} len=strlen(name);
for(i=;environ[i]!=NULL;i++){
if((strncmp(name,environ[i],len)== && (environ[i][len]=='=')){
strcpy(envbuf,&environ[i][len+]);
pthread_mutex_unlock(&env_mutex);
return (envbuf);
}
}
pthread_mutex_unlock(&env_mutex);
return (NULL);
}

四.取消选项

线程属性有两个属性并没有包含在pthread_attr_t结构中,它们是可取消状态和可取消类型,这两个属性影响着线程在响应pthread_cancel函数调用时可呈现的行为.

可取消状态属性可以是PTHREAD_CANCEL_ENABLE和PTHREAD_CANCEL_DISABLE,线程可以通过调用pthread_setcancelstate修改它的可取消状态.

pthread_cancel调用并不等待线程终止,在默认情况下,线程在取消请求发出以后还是继续运行,直到线程到达某个取消点,取消点是线程检测是否被取消并按照请求进行动作的一个位置。

int pthread_setcancelstate(int state,int *oldstate);

void pthread_testcancel(void);

int pthread_setcanceltype(int type,int *oldtype);

五.线程与信号

每个线程都有自己的信号屏蔽字,但是信号的处理程序是进程中所有线程共享的,这意味着尽管单个线程可以组织某些信号,但当线程修改了与某个信号相关的处理行为以后,所有线程都必须共享这个处理行为的改变。

int pthread_sigmask(int how,const sigset_t* set,sigset_t oset);

int sigwait(const sigset_t *set,int *signop);

如果信号集中的某个信号在sigwait调用的时候处于未决状态,那么sigwait将无阻塞地返回,在返回之前,sigwait将从进程中移除那些处于未决状态的信号。为了避免错误动作发生,线程在调用sigwait之前,必须阻塞那些它正在等待的信号。

int pthread_kill(pthread_t thread,int signo);

《APUE》读书笔记第十二章-线程控制的更多相关文章

  1. 《UNIX环境高级编程》(APUE) 笔记第十二章 - 线程控制

    12 - 线程控制 GitHub 地址 1. 线程限制 下图为与 线程操作 有关的一些 限制: 可以通过 sysconf 函数进行查询 . 2. 线程属性 可使用 pthread_attr_t 结构修 ...

  2. apue学习笔记(第十二章 线程控制)

    本章将讲解控制线程行为方面的详细内容,而前面的章节中使用的都是它们的默认行为 线程属性 pthread接口允许我们通过设置每个对象关联的不同属性来细调线程和同步对象的行为.管理这些属性的函数都遵循相同 ...

  3. C primer plus 读书笔记第十二章

    C的强大功能之一在于它允许我们控制程序的细节.C的内存管理系统正是这种控制能力的例子.它通过让我们决定哪些函数知道哪些变量以及一个变量在程序中存在多长时间来实现这些控制. 1.存储类及其说明符 主要的 ...

  4. java编程思想读书笔记 第十二章 通过异常处理错误(下)

    1.异常的限制 当覆盖方法的时候,仅仅能抛出在基类方法的异常说明里列出的那些异常. 这意味着,当基类使用的代码应用到其派生类对象的时候,一样能够工资,异常也不例外. 以下的样例是在编译时施加在异常上面 ...

  5. Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验

    Android群英传笔记--第十二章:Android5.X 新特性详解,Material Design UI的新体验 第十一章为什么不写,因为我很早之前就已经写过了,有需要的可以去看 Android高 ...

  6. 《Linux内核设计与实现》读书笔记——第一、 二章

    <Linux内核设计与实现>读书笔记--第一. 二章 标签(空格分隔): 20135321余佳源 第一章 Linux内核简介 1.Unix内核特点 十分简洁:仅提供几百个系统调用并且有明确 ...

  7. “全栈2019”Java第二十二章:控制流程语句中的决策语句if-else

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  8. [CSAPP笔记][第十二章并发编程]

    第十二章 并发编程 如果逻辑控制流在时间上是重叠,那么它们就是并发的(concurrent).这种常见的现象称为并发(concurrency). 硬件异常处理程序,进程和Unix信号处理程序都是大家熟 ...

  9. o'Reill的SVG精髓(第二版)学习笔记——第十二章

    第十二章 SVG动画 12.1动画基础 SVG的动画特性基于万维网联盟的“同步多媒体集成语言”(SMIL)规范(http://www.w3.org/TR/SMIL3). 在这个动画系统中,我们可以指定 ...

随机推荐

  1. Applet: 用HTML调用Applet的几个注意事项

    问题:HTML找不到java class. 首先,如果xxx.java文件与HTML文件在同一目录下,直接运行cmd-javac 该 xxx.java文件,生成xxx.class文件.HTML中的&l ...

  2. 详解ios文件系统文件目录读写操作-备用

    iPhone文件读写系统操作教程是本文要介绍的内容,对于一个运行在iPhone得app,它只能访问自己根目录下得一些文件(所谓sandbox).一个app发布到iPhone上后,它得目录结构如下:  ...

  3. SQL Server 2012数据库还原所遇到的问题

    在SQL Server2005及以下版本做数据库备份还原时,需要首先建立数据库,然后才能进行数据库还原操作:而在SQL Server2005以上版本做数据库还原时,不需要建立数据库,可以直接进行数据库 ...

  4. hdu 4544 湫湫系列故事——消灭兔子

    http://acm.hdu.edu.cn/showproblem.php?pid=4544 优先队列+贪心. #include <cstdio> #include <queue&g ...

  5. JavsScript中的Document对象

    Document对象的属性 alinkColor,linkColor,vlinkColor:这些属性描述了超链接的颜色.linkColor指未访问过的链接的正常颜色,vlinkColor指访问过的链接 ...

  6. BZOJ3400: [Usaco2009 Mar]Cow Frisbee Team 奶牛沙盘队

    3400: [Usaco2009 Mar]Cow Frisbee Team 奶牛沙盘队 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 89  Solve ...

  7. HDU_2056——相交矩形的面积

    Problem Description Given two rectangles and the coordinates of two points on the diagonals of each ...

  8. Gradle[1]gradle distZip时,增加目录信息到zip中

    在使用gradle distZip打包生成应用程序时,发现只把src目录,及依赖的本地libs目录下的Jar包达到生成zip中, 如果项目还需要一些库文件,在和src同级目录,如sigar目录下,而且 ...

  9. js中的循环语句

    js中的循环语句可分为三种:1.while:2.do……while:3.for. while的语法为 while (exp) {    //statements;} var a=1,b=0; whil ...

  10. AvalonEdit 对于选定的文本添加前缀和后缀

    1: /// <summary> 2: /// 两边追加标志 3: /// </summary> 4: /// <param name="syntax" ...