12 Thread Control
1 Thread Limits
用sysconf函数可以获得和thread相关的一些系统信息,主要是线程相关的一些最大值:
NAME
|
Description
|
Argument
|
PTHREAD_DESTRUCTOR_ITERATIONS
|
最大尝试销毁线程相关数据(Thread Specific Data)的次数,见下面关于Thread-Specific Data的内容
|
_SC_THREAD_DESTRUCTOR_ITERATIONS
|
PTHREAD_KEYS_MAX
|
一个进程所能够创建的最大键数
|
_SC_THREAD_KEYS_MAX
|
PTHREAD_STACK_MIN
|
线程栈的最小值
|
_SC_THREAD_STACK_MIN
|
PTHREAD_THREADS_MAX
|
单个进程中的线程个数最大值
|
_SC_THREAD_THREADS_MAX
|
部分概念在后面会提到。
虽然标准定义了这些常量,不过在很多系统上面可能根本就没有定义对应的Argument(如_SC_THREAD_DESTRUCTOR_ITERATIONS可能未定义),或者sysconf函数返回错误。因此在很多时候这些很难派上用场。
2 Thread Attributes
在前面讲到pthread_create等函数的时候,这些函数有一个参数pthread_attr_t。缺省情况下可以传NULL。但是如果想自己定义线程的相关属性的话,应该调用pthread_attr_init函数来定义:
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
返回0表示正常,出错时返回错误值
|
pthread_attr_init函数负责初始化pthread_attr_t结构为缺省值。pthread_attr_destroy负责释放在pthread_attr_init函数调用时分配的内存,同时将pthread_attr的内容置为非法。如果要修改属性,需要调用其他函数来手动设置。
基本的线程属性如下:
Name
|
Description
|
detachstate
|
detached状态,在前一章中有讲述
|
guardsize
|
线程栈底部的Guard缓冲区的大小
|
stackadddr
|
线程栈的最低地址
|
stacksize
|
线程栈的大小
|
1. Detached State:一个线程如果出于Detached状态,说明此线程在退出的时候可以立刻释放其资源和对应的结束代码,从而无法使用pthread_join。可以用pthread_attr_setdetachedstate函数来设置Detach状态。传入PTHREAD_CREATE_DETACHED可以让线程启动的时候就处于Detached状态,而传入PTHREAD_CREATE_JOINABLE则是以通常状态启动线程
#include <pthread.h>
int pthread_attr_getdetachedstate(const pthread_attr_t *restrict attr, int *detachstate);
int pthread_attr_setdetachedstate(pthread_attr_t *restrict attr, int detachstate);
返回0表示正常,出错时返回错误值
|
2. GuardSize:在线程栈的末尾有一个比较小的内存区域,这个内存区域是保护起来的,一旦栈发生overflow,系统立刻就会知道,发送一个Signal(Windows也有类似的功能,只不过是用于自动增长栈的大小)。缺省情况下这个大小正好是一个页=PAGESIZE。甚至可以用函数将该数值设置为0来禁止这个功能。如果我们修改了栈地址的话,系统会认为我们会自己处理Overflow的问题,因此也不会提供这个功能。调用pthread_attr_get_guardsize & pthread_attr_set_guardsize可以获得/设置这个值:
#include <pthread.h>
int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, size_t *restrict guardsize);
int pthread_attr_setguardsize(pthread_attr_t *restrict attr, size_t guardsize);
返回0表示正常,出错时返回错误值
|
3. StackSize:线程可以自己设置栈的大小,用pthread_attr_getstacksize和pthread_attr_setstacksize:
#include <pthread.h>
int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, size_t *restrict stacksize);
int pthread_attr_setguardsize(pthread_attr_t *restrict attr, size_t stacksize);
返回0表示正常,出错时返回错误值
|
4. StackAddr:当进程中线程过多的时候,有可能会栈空间不足。一个方案是用malloc或者nmap来分配新的内存,作为一个另外的栈,供线程使用。可以调用pthread_attr_setstack和pthread_attr_getstack来获得/设置:
#include <pthread.h>
int pthread_attr_getstack(const pthread_attr_t *restrict attr, void **restrict stackaddr, size_t *restrict stacksize);
int pthread_attr_setstack(pthread_attr_t *restrict attr, void *stackaddr, size_t *stacksize)
返回0表示正常,出错时返回错误值
|
除此之外,还有其他一些线程属性:
1. Cancellability State
2. Cancellability Type
3. Concurrency Level
1和2会在第6节中讲述。
Concurrency Level定义了用户模式线程和内核线程/进程之间的对应关系。如果具体操作系统实现是按照1对1,也就是一个用户模式线程对应一个内核模式线程的话,那么修改这个值没有作用。但是如果操作系统实现用少量内核模式线程/进程来模拟用户模式线程的话,那么修改这个值可能会提高或者降低程序和系统的性能。Level值并没有具体的意义,只是一个hint。Level=0表示让系统自动选择。函数原型如下:
#include <pthread.h>
int pthread_attr_getconcurrency(void);
int pthread_attr_setconcurrency(int level);
返回0表示正常,出错时返回错误值
|
注意这个属性不是和具体线程相关的,而是系统级别的。
3 Synchronization Attributes
同步对象也有他们自己的Attributes。
3.1 Mutex Attributes
Mutex的属性类型为pthread_mutexattr_t。可以用pthread_mutexattr_init和pthread_mutexattr_destroy来创建和释放Mutex Attributes。类似的,pthread_mutexattr_init函数会将结构初始化为缺省值。
#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
返回0表示正常,出错时返回错误值
|
Mutex的属性有:
1. Process-Shared:指定Mutex是否为多个进程所共享。缺省值是PTHREAD_PROCESS_PRIVATE,即只有创建者进程才可以访问此Mutex。也可以设置为PTHREAD_PROCESS_SHARED,在多个进程之间共享。
#include <pthread.h>
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
返回0表示正常,出错时返回错误值
|
2. Type:指定Mutex的类型。Mutex有下列类型:
Type
|
未释放锁的情况获得锁
|
未获得锁情况下释放锁
|
已释放锁的情况下再次释放
|
Description
|
PTHREAD_MUTEX_NORMAL
|
死锁
|
未定义
|
未定义
|
一般的Mutex
|
PTHREAD_MUTEX_ERRORCHECK
|
出错
|
出错
|
出错
|
加强错误检查
|
PTHREAD_MUTEX_RECURSIVE
|
允许
|
出错
|
出错
|
允许单个线程获得锁多次,需要多次释放,但是不能超过获得锁的次数。一般用来处理可重入的函数,见下面一章
|
PTHREAD_MUTEX_DEFAULT
|
未定义
|
未定义
|
未定义
|
完全没有错误检查
|
通过调用pthread_mutexattr_gettype & pthread_mutexattr_settype来获得/设置对应的type:
#include <pthread.h>
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr, int *restrict type);
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
返回0表示正常,出错时返回错误值
|
3.2 Reader-Writer Lock Attributes
类似Mutex Attributes,但是只支持Process Shared属性。
3.3 Condition Variable Attributes
类似Mutex Attributes,但是只支持Process Shared属性。
4 Reentrancy
1. 大部分Single UNIX Specification所定义的函数都是线程安全的,但是也有不少例外。实际使用的时候建议参考文档,确定函数是否是线程安全。
2. 文件支持用ftrylockfile, flockfile, funlockfile来锁定文件访问。标准IO函数被要求必须调用在内部实现中调用flockfile, funlockfile。基于字符的部分IO函数具有非线程安全版本,以_unlocked结尾,如:getchar_unlocked, getc_unlocked, putchar_unlocked, putc_unlocked
3. 书中提供了一个可重入的getenv_r实现。要点是:
a. 用到了Recursive Mutex(使用pthread_mutexattr_settype函数调用设置)来保护自己和其他线程冲突(普通的Mutex就可以做到),同时允许重入(必须用Recursive Mutex)
b. 要求调用者提供自己的buffer,而不是用静态全局变量envbuf来访问结果
c. 使用pthread_once函数保证只调用一个初始化函数一遍,用于初始化Mutex(当然用其他方法也可以)
5 Thread-Specific Data
1. Thread-Specific Data是一种很方便的将数据和线程联系起来的方法,在C Runtime中也大量用到Thread-Specific Data来维护线程相关的数据,一个典型的例子是errno:实际上errno是一个函数调用,返回和线程相关的错误值。Windows中有类似的机制,称为TLS (Thread Local Storage)
2. 访问Thread-Specific Data需要使用Key。不同线程使用同一个key访问同一类型的数据(比如Errno),但是可以存放不同的值。Key的类型为pthread_key_t
3. 用pthread_key_create函数创建key:
#include <pthread.h>
int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *))
返回0表示正常,出错时返回错误值
|
创建好之后key对应的Thread-Specific Data为NULL。
Destructor函数指针指定当pthread_key_t被删除的时候需要自动调用的函数,可以传NULL。参数值为TSD的具体值,必然非NULL。在线程正常退出时候,如return或者pthread_exit,当数据值为非NULL时候会调用。但是当线程非正常退出,如调用exit, _exit, _Exit, abort或者其他非正常退出的时候,destructor不会被调用。一般情况下,这个destructor用来销毁用户用malloc为Thread-Specific Data分配的空间。注意:一般不应该用destructor来调用pthread_key_delete,因为delete对于一个key只用调一次,而destructor是对每个线程都调用的,前提是线程正常退出并且TSD不为NULL。
Key的总数量可能会有限制。可以用PTHREAD_KEYS_MAX来查询最大值。
因为调用Destructor的时候这个Destructor可能又会创建新的Key,所以当线程退出的时候,调用Destructor的过程会反复继续直到没有key具有非NULL值或者次数到达最大值PTHREAD_DESTRUCTOR_ITERATIONS为止。这个值可以用sysconf获得。
4. 用pthread_key_delete函数删除key:
#include <pthread.h>
int pthread_key_delete(pthread_key_t *keyp)
返回0表示正常,出错时返回错误值
|
注意,调用此函数不会导致Destructor被调用!
5. 可以用pthread_once函数保证某个函数只被调一次,用法如下:
padding-right: 5.4pt; padding-left: 5.4pt; border-left-color: #e0dfe3; border-bottom-color: #e0dfe3; padding-bottom: 0in; width: 621pt; border-
分享到:
Global site tag (gtag.js) - Google Analytics
|
相关推荐
多线程编程指南,SUN的Pthread线程库手册,中文版的。
对于C++而言,当我们需要使用多线程时,可以使用boost::thread库或者自从C++ 11开始支持的std::thread,也可以使用操作系统相关的线程API,如在Linux上,可以使用pthread库。除此之外,还可以使用omp来使用多线程。...
Linux下多线程编程-Pthread与Semaphore的使用.doc
编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。顺便说一下,Linux下pthread的实现是通过系统调用clone()来实现的。clone()是Linux所特有的系统调用,它的使用方式类似fork...
linux 多线程编程 pthread 中文文档 已经添加目录
pthread多线程编程,进行了封装,方便使用。
java、win32、pthread三种线程库均有 适合用来学习多线程操作的入门例程
pthread资源包,pthread源码和已经编译好的VS2019_x64版本 #include <pthread.h> pthread_t newThread; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS); ...
运行这个文件选择Extract,会出现Pre-built.2,pthreads.2,QueueUserAPCEx这三个文件,然后选取Pre-built.2文件中的include和lib文件,把它们放到对应C++安装目录中的include和lib文件中,即D:\Program Files (x86)...
(1)实现多线程字符输出,存在三个线程,线程1用来监听用户输入和其他两个线程的监听器,线程2的主要内容为每隔一段时间,输出“hello 2”字符串,线程3的主要内容为每隔一段时间,输出“hello 3”字符串。...
linux多线程编程指南
Linux下使用pthread库编写的简单的多线程程序,在调用线程时绑定了内核
主要是UNIX下关于PTHREAD的多线程编程指南
我们进行多线程编程,可以有多种选择,可以使用WindowsAPI,如果你在使用GTK,也可以使用GTK实现了的线程库,如果你想让你的程序有更多的移植性你好是选择POSIX中的Pthread函数库,我的程序是在Linux下写的,所以我...
pthread 开发库源码
Linux系统下采用多线程方案的Socket编程实现了服务端和客户端的通信
使用pthread库实现openssl多线程ssl服务端和客户端,大家参考使用吧
linux下pthread的多线程编程+代码,适合初学者
多线程编程:互斥锁使用。 打包文件包含两个文件:c文件源代码、Makefile文件,运行环境在Ubuntu14.04下,使用自带的gcc编译器,同学们只需将文件夹复制到某一目录下之后在终端执行:1.“make”生成“test”可执行...