一次性初始化
有些事需要且只能执行一次(比如互斥量初始化)。通常当初始化应用程序时,可以比较容易地将其放在main函数中。但当你写一个库函数时,就不能在main里面初始化了,你可以用静态初始化,但使用一次初始(pthread_once_t)会比较容易些。
首先要定义一个pthread_once_t变量,这个变量要用宏PTHREAD_ONCE_INIT初始化。然后创建一个与控制变量相关的初始化函数。
pthread_once_t once_control = PTHREAD_ONCE_INIT;
void init_routine()
{
//初始化互斥量
//初始化读写锁
…
}
接下来就可以在任何时刻调用pthread_once函数
pthread_once函数: 一次性初始化
原函数:int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
参数:once_control,初始化函数调用情况的标志
(*init_routine),初始化执行函数
返回值:成功返回0,失败返回错误码
功能:此函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。在多线程编程环境下,尽管pthread_once()调用会出现在多个线程中,init_routine()函数仅执行一次,究竟在哪个线程中执行是不定的,是由内核调度来决定。
Linux Threads使用互斥锁和条件变量保证由pthread_once()指定的函数执行且仅执行一次。实际"一次性函数"的执行状态有三种:
NEVER(0)、IN_PROGRESS(1)、DONE (2),用once_control来表示pthread_once()的执行状态:
1)如果once_control初值为0,那么 pthread_once从未执行过,init_routine()函数会执行。
2)如果once_control初值设为1,则由于所有pthread_once()都必须等待其中一个激发"已执行一次"信号, 因此所有pthread_once ()都会陷入永久的等待中,init_routine()就无法执行
3)如果once_control设为2,则表示pthread_once()函数已执行过一次,从而所有pthread_once()都会立即 返回,init_routine()就没有机会执行
当pthread_once函数成功返回,once_control就会被设置为2
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
pthread_t tid;
pthread_once_t once=PTHREAD_ONCE_INIT;//通过宏定义表明一次性初始化函数的执行状态
void thread_init()//一次性初始化处理函数
{
printf("thread 0x%x run init\n",tid);
printf("in init:once is %d\n",once);
}
void *thread_fun1(void *arg)
{
tid = pthread_self();//得到自己的tid号
printf("thread 0x%x\n",tid);
printf("once is %d\n",once);
pthread_once(&once,thread_init);//调用一次性初始化函数
printf("once is %d\n",once);
return NULL;
}
void *thread_fun2(void *arg)
{
sleep(2);
tid = pthread_self();//获得自己tid号
printf("thread 0x%x\n",tid);
// printf("once is %d\n",once);
pthread_once(&once,thread_init);//调用一次性初始化函数
// printf("once is %d\n",once);
return NULL;
}
int main()
{
pthread_t tid1, tid2;
int err;
//创建新的线程
err = pthread_create(&tid1, NULL, thread_fun1, NULL);
if(err != 0)
{
printf("create new thread 1 failed\n");
return 1;
}
//创建新的线程
err = pthread_create(&tid2, NULL, thread_fun2, NULL);
if(err != 0)
{
printf("create new thread 2 failed\n");
return 1;
}
//链接新的线程
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
运行结果
thread 0x6aba7700
once is 0
thread 0x6aba7700 run init
in init:once is 1
once is 2
thread 0x6a3a6700
线程的分离属性
线程的属性用pthread_attr_t类型的结构表示,在创建线程的时候可以不用传入NULL,而是传入一个pthread_attr_t结构,由用户自己来配置线程的属性。pthread_attr_t类型对应用程序是不透明的,也就是说应用程序不需要了解有关属性对象内部结构的任何细节,因而可以增加程序的可移植性。
线程属性:
detachstate 线程的分离状态
guardsize 线程栈末尾的警戒区域大小(字节数)
stackaddr 线程栈的最低地址
stacksize 线程栈的大小(字节数)
并不是所有的系统都支持线程的这些属性,因此你需要检查当前系统是否支持你设置的属性。当然还有一些属性不包含在pthread_attr_t结构中,例如:线程的可取消状态、取消类型、并发度
pthread_attr_init函数: 线程属性初始化
原函数:int pthread_attr_init(pthread_attr_t *attr)
参数:attr,要初始化的属性
返回值:成功返回0 ,失败返回错误码
pthread_attr_destory函数: 线程属性销毁
原函数:int pthread_attr_destroy(pthread_attr_t *attr)
参数:attr,要销毁的属性
返回值:成功返回0 ,失败返回错误码
如果在调用pthread_attr_init初始化属性的时候分配了内存空间,那么pthread_attr_destroy将释放内存空间。除此之外,pthread_atty_destroy还会用无效的值初始化pthread_attr_t对象,因此如果该属性对象被误用,会导致创建线程失败。
注: pthread_attr_t结构在使用之前需要初始化,使用完之后需要销毁
分离一个正在运行的线程并不影响它,仅仅是通知当前系统该线程结束时,其所属的资源可以回收。一个没有被分离的线程在终止时会保留它的虚拟内存,包括他们的堆栈和其他系统资源,有时这种线程被称为“僵尸线程”。创建线程时默认是非分离的。如果线程具有分离属性,线程终止时会被立刻回收,回收将释放掉所有在线程终止时未释放的系统资源和进程资源,包括保存线程返回值的内存空间、堆栈、保存寄存器的内存空间等。
如果在创建线程的时候就不需要了解线程的终止状态,那么可以修改pthread_attr_t结构体的detachstate属性,让线程以分离状态启动。
pthread_attr_setdetachstate函数: 设置线程的分离属性
原函数:int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
参数:attr,要设置的分离状态属性
detachstate,状态设置(有两种值:PTHREAD_CREATE_DETACHED分离的、PTHREAD_CREATE_JOINABLE 非分离的,可连接的)
返回值:成功返回0 ,失败返回错误码
pthread_attr_getdetachstate函数: 分离状态属性获取函数
原函数:int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate)
参数:attr,要设置的分离状态属性
detachstate,已经设置的分离状态属性
返回值:成功返回0 ,失败返回错误码
设置线程分离属性的步骤
1、定义线程属性变量pthread_attr_t attr
2、初始化attr,pthread_attr_init(&attr)
3、设置线程为分离或非分离 pthread_attr_setdetachstate(&attr, detachstate)
4、创建线程pthread_create(&tid, &attr, thread_fun, NULL)
注:所有的系统都会支持线程的分离状态属性
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
void *thread_fun1(void *arg)
{
printf("new thread 1\n");
return (void *)1;
}
void *thread_fun2(void *arg)
{
printf("new thread 2\n");
return (void *)2;
}
int main()
{
pthread_t tid1, tid2;
int err;
int flag;
//定义属性变量
pthread_attr_t attr;
//初始化属性
pthread_attr_init(&attr);
//设置分离状态属性,置为分离的
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
//获取分离状态的属性
pthread_attr_getdetachstate(&attr,&flag );
printf("flag is %d\n",flag);
//创建新的线程 1
err = pthread_create(&tid1, &attr, thread_fun1, NULL);
if(err)
{
printf("create new thread 1 failed\n");
return;
}
//创建新的线程 2
err = pthread_create(&tid2, NULL, thread_fun2, NULL);
if(err)
{
printf("create new thread 2 failed\n");
return;
}
//连接线程1
err = pthread_join(tid1, NULL);
if(!err)
printf("join thread 1 success\n");
else
printf("join thread 1 failed\n");
//连接线程2
err = pthread_join(tid2, NULL);
if(!err)
printf("join thread 2 success\n");
else
printf("join thread 2 failed\n");
//销毁attr
pthread_attr_destroy(&attr);
return 0;
}
运行结果
flag is 1
join thread 1 failed
new thread 1
new thread 2
join thread 2 success
栈属性
对于进程来说,虚拟地址空间的大小是固定的,进程中只有一个栈,因此它的大小通常不是问题。但对线程来说,同样的虚拟地址被所有的线程共享。如果应用程序使用了太多的线程,致使线程栈累计超过可用的虚拟地址空间,这个时候就需要减少线程默认的栈大小。另外,如果线程分配了大量的自动变量或者线程的栈帧太深,那么这个时候需要的栈要比默认的大。如果用完了虚拟地址空间,可以使用malloc或者mmap来为其他栈分配空间,并修改栈的位置。
pthread_attr_setstack函数: 修改栈的属性
原函数:int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize)
参数:attr,栈属性
stackaddr,栈的内存单元最低地址
stacksize,栈的大小
返回值:成功返回0,失败返回错误码
phread_attr_getstack函数: 获取栈属性
原函数:int pthread_attr_getstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize)
参数:attr,栈属性
stackaddr,栈的内存单元最低地址
stacksize,栈的大小
返回值:成功返回0,失败返回错误码
参数stackaddr是栈的内存单元最低地址,参数stacksize是栈的大小。要注意stackaddr并不一定是栈的开始,对于一些处理器,栈的地址是从高往低的,那么这时stackaddr是栈的结尾。
当然也可以单独获取或者修改栈的大小,而不去修改栈的地址。对于栈大小设置,不能小于PTHREAD_STACK_MIN(需要头文件limit.h)
pthread_attr_setstacksize函数: 设置栈的大小
原函数:int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
参数:attr,栈属性
stacksize,要设置的栈大小
返回值:成功返回0,失败返回错误码
pthread_attr_getstacksize函数: 获取栈的大小
原函数:int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize)
参数:attr,栈属性
stacksize,要获取的栈大小
返回值:成功返回0,失败返回错误码
对于栈大小的设置,在创建线程之后,还可以修改。
对于遵循POSIX标准的系统来说,不一定要支持线程的栈属性,因此需要检查
1)在编译阶段使用
_POSIX_THREAD_ATTR_STACKADDR 和 _POSIX_THREAD_ATTR_STACKSIZE符号来检查系统
_POSIX_THREAD_ATTR_STACKSIZE符号来检查系统在/usr/include/bits/posix_opt.h(可以用#ifdef和#endif来实现)
2)在运行阶段
把_SC_THREAD_ATTR_STACKADD和 _SC_THREAD_THREAD_ATTR_STACKSIZE传递给sysconf函数检查系统对线程栈属性的支持
线程属性guardsize控制着线程栈末尾以后用以避免栈溢出的扩展内存的大小,这个属性默认是PAGESIZE个字节。你可以把它设为0,这样就不会提供警戒缓冲区。同样的,如果你修改了stackaddr,系统会认为你自己要管理栈,警戒缓冲区会无效。
pthread_attr_setguardsize函数: 设置设置guardsize
原函数:int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)
参数:attr,栈属性
guardsize,扩展内存的大小
返回值:成功返回0,失败返回错误码
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
pthread_attr_t attr;
void *thread_fun(void *arg)
{
size_t stacksize;
//判断系统是否支持
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
pthread_attr_getstacksize(&attr,&stacksize);//获得属性栈的大小
printf("new thread stack size is %d\n",stacksize);
pthread_attr_setstacksize(&attr,16684);//设置属性栈的大小
pthread_attr_getstacksize(&attr,&stacksize);//获得属性栈的大小
printf("new thread stack size is %d\n",stacksize);
#endif
return (void *)1;
}
int main()
{
pthread_t tid;
int err;
//属性初始化函数
pthread_attr_init(&attr);
//设置线程的分离属性
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
//判断系统是否支持
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);//设置属性栈的大小
#endif
//创建新的线程
err = pthread_create(&tid, &attr, thread_fun, NULL);
if(err)
{
printf("create new thread failed\n");
return;
}
//链接新的线程
pthread_join(tid,NULL);
return 0;
}
运行结果
new thread stack size is 16384
new thread stack size is 16684
同步属性
就像线程有属性一样,线程的同步互斥量也有属性,比较重要的是进程共享属性和类型属性。互斥量的属性用pthread_mutexattr_t类型的数据表示,当然在使用之前必须进行初始化,使用完成之后需要进行销毁。
pthread_mutexattr_init函数: 互斥量的属性初始化函数
原函数:int pthread_mutexattr_init(pthread_mutexattr_t*attr)
参数:attr,属性
返回值:成功返回0,失败返回错误码
pthread_mutexattr_destroy函数: 互斥量销毁函数
原函数:int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
参数:attr,属性
返回值:成功返回0,失败返回错误码
进程共享属性有两种值:
PTHREAD_PROCESS_PRIVATE,这个是默认值,同一个进程中的多个线程访问同一个同步对象;
PTHREAD_PROCESS_SHARED, 这个属性可以使互斥量在多个进程中进行同步,如果互斥量在多进程的共享内存区域,那么具有这个属性的互斥量可以同步多进程。
pthread_mutexattr_setpshared函数: 设置互斥量进程共享属性
原函数:int pthread_mutexattr_setpshared(const pthread_mutexattr_t *attr, int pshared)
参数:attr,共享属性
pshared,设置是否共享
返回值:成功返回0,失败返回错误码
pthread_mutexattr_getpshared函数: 获得互斥量进程共享属性
原函数:int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared)
参数:attr,共享属性
pshared,获得属性
返回值:成功返回0,失败返回错误码
注: 进程共享属性需要检测系统是否支持,可以检测宏_POSIX_THREAD_PROCESS_SHARED(#ifdef和#endif检测)
类型属性
pthread_mutexattr_settype函数: 设置互斥量的类型属性
原函数:int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
参数:attr,类型属性
type,互斥量类型
返回值:成功返回0,失败返回错误码
pthread_mutexattr_gettype函数: 获取互斥量的类型属性
原函数:int pthread_mutexattr_gettype(pthread_mutexattr_t *restrict attr, int *restrict type)
参数:attr,类型属性
type,互斥量类型
返回值:成功返回0,失败返回错误码
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
int main()
{
char *shm = "myshm";
char *shm1 = "myshm1";
int shm_id,shm_id1;
char *buf;
pid_t pid;
pthread_mutex_t *mutex;//互斥量
pthread_mutexattr_t mutexattr;//互斥量属性
//打开共享内存
shm_id1=shm_open(shm1, O_RDWR|O_CREAT, 0644);
//调整共享内存的大小
ftruncate(shm_id1,100);
//映射共享内存,MAP_SHARED属性表明,对共享内存的任何修改都会影响其他进程
mutex = (pthread_mutex_t *)mmap(NULL,100,PROT_READ|PROT_WRITE, MAP_SHARED, shm_id1, 0);
//属性初始化
pthread_mutexattr_init(&mutexattr);
//检测系统是否支持
#ifdef _POSIX_THREAD_PROCESS_SHARED
//设置共享
pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED);
#endif
//初始化互斥量
pthread_mutex_init(mutex,&mutexattr);
//打开共享内存
shm_id = shm_open(shm, O_RDWR|O_CREAT, 0644);
//调整共享内存大小
ftruncate(shm_id, 100);
//映射共享内存,MAP_SHARED属性表明,对共享内存的任何修改都会影响其他进程
buf =(char *)mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, shm_id, 0);
//创建父子进程
pid = fork();
if(pid==0)
{
//休眠1s,让父进程先运行
sleep(1);
printf("child proccess\n");
//互斥量加锁
pthread_mutex_lock(mutex);
//将共享内存内存修改为hello
memcpy(buf, "hello", 6);
printf("child buf is : %s\n", buf);
//互斥量解锁
pthread_mutex_unlock(mutex);
}
else if(pid>0)
{
printf("parent proccess\n");
//互斥量加锁
pthread_mutex_lock(mutex);
//修改共享内存到内容,改为world
memcpy(buf, "world", 6);
sleep(3);
printf("parent buf is : %s\n", buf);
//互斥量解锁
pthread_mutex_unlock(mutex);
}
//销毁属性
pthread_mutexattr_destroy(&mutexattr);
//销毁互斥量
pthread_mutex_destroy(mutex);
//解除映射
munmap(buf, 100);
//消除共享内存
shm_unlink(shm);
//解除映射
munmap(mutex, 100);
//消除共享内存
shm_unlink(shm1);
}
编译
gcc -o pthread_mutexattr pthread_mutexattr.c -lpthread -lrt
运行结果
parent proccess
child proccess
parent buf is : world
child buf is : hello
读写锁也有属性,它只有一个进程共享属性
pthread_rwlockattr_init函数: 读写锁属性初始化函数
原函数:int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
参数:attr,属性
返回值:成功返回0,失败返回错误码
pthread_rwlockattr_destroy函数: 读写锁属性销毁
原函数:int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
参数:attr,属性
返回值:成功返回0,失败返回错误码
pthread_rwlockattr_setpshared函数: 设置读写锁进程共享属性
原函数:int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared)
参数:attr,共享属性
pshared,设置是否共享
返回值:成功返回0,失败返回错误码
pthread_rwlockattr_getpshared函数: 获取读写锁进程共享属性
原函数:int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int *restrict pshared)
参数:attr,共享属性
pshared,获取属性
返回值:成功返回0,失败返回错误码
条件变量也有进程共享属性
pthread_condattr_init函数: 条件变量属性初始化函数
原函数:int int pthread_condattr_init(pthread_condattr_t *attr)
参数:attr,属性
返回值:成功返回0,失败返回错误码
pthread_condattr_destroy函数: 条件变量属性销毁
原函数:int pthread_condattr_destroy(pthread_condattr_t *attr)
参数:attr,属性
返回值:成功返回0,失败返回错误码
pthread_condattr_setpshared函数: 设置条件变量属性函数
int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared)
参数:attr,共享属性
pshared:设置是否共享
返回值:成功返回0,失败返回错误码
pthread_condattr_getpshared函数: 获取条件变量属性
原函数:int pthread_condattr_getpshared(const pthread_condattr_t *restrict attr, int *restrict pshared)
参数:attr,共享属性
pshared,获取的共享
返回值:成功返回0,失败返回错误码