Linux中线程的高级控制-一次性初始化、线程的分离属性、栈属性及同步属性

一次性初始化
  有些事需要且只能执行一次(比如互斥量初始化)。通常当初始化应用程序时,可以比较容易地将其放在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,失败返回错误码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值