线程控制之高级属性——互斥量,锁等功能实现

内容:
1,什么是一次性初始化?


例子1:pthread_once函数的一次性初始化验证:

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"

pthread_once_t once = PTHREAD_ONCE_INIT;//声明全局变量,确保在本进程中即被执行一次
pthread_t tid;//全局的线程id变量
//可以在该函数里初始化互斥量,读写锁
//这里不做任何初始化,仅仅打印是哪个线程调用了它
void init_routine()
{
    printf("init_routine used by thread 0x%ld\n",tid);
}
 //线程1
void *pthread_fun1(void *arg)
{
    tid = pthread_self();//获取自身tid
    printf("this is thread1 0x%ld\n",tid);
    printf("once 1 is %ld\n",once);
    pthread_once(&once,init_routine);//调用初始化函数
    printf("once 1 is %ld\n",once);
    
    sleep(1);   
    return (void*)1;
}
 
void *pthread_fun2(void *arg)
{
    sleep(2);
    tid = pthread_self();//获取自身tid
    printf("this is thread2 0x%ld\n",tid);
    printf("once 2 is %ld\n",once);
    pthread_once(&once,init_routine);//调用初始化函数
    printf("once 2 is %ld\n",once);
    
    sleep(1);   
    return (void*)2;
}
 
int main()
{

    pthread_t tid1,tid2;
    int errno1,errno2;
    int jval_1,jval_2;
    int *rval_1,*rval_2;
    
    errno1=pthread_create(&tid1,NULL,pthread_fun1,NULL);
    if(errno1 !=0)
    {
        printf("create new thread1 failure\n");
        return errno1;
     }
     
     errno2=pthread_create(&tid2,NULL,pthread_fun2,NULL);
     if(errno2 !=0)
     {
        printf("create new thread2 failure\n");
        return errno2;
     }
    
    sleep(1);
    jval_1=pthread_join(tid,&rval_1);//等待新线程1执行完成1
    sleep(1);
    jval_2=pthread_join(tid,&rval_2);//等待新线程2执行完成
 
    sleep(2);   
    printf("jval_1=%d\n",jval_1);//连接线程1的函数返回值
    printf("jval_2=%d\n",jval_2);//连接线程2的函数返回值
    printf("rval_1=%d\n",(int*)rval_1);//线程1的返回值
    printf("rval_2=%d\n",(int*)rval_2);//线程2的返回值
        
    return 0;
}

运行结果如下图:
1)当once_control值为PTHREAD_ONCE_INIT时

如结果所示,在新线程thread1中pthread_once()函数调用初始化函数init_routine()进行初始化,当init_routine()被thread1调用之前值为0调用之后值为2,once的值变为2即不能再被其他线程调用(一次性初始化的功能);再等到线程2调用pthread_once函数时,此时once值为2已经无法调用初始化函数init_routine来为其线程做初始化操作了。
2)当once_control值为1时,

拥有pthread_once函数的线程thread1和thread2陷入永久等待,无法执行init_routine函数。
2,线程的属性
线程的几种属性

1)线程属性初始化和销毁

2)分离属性
分离在线程结束时会释放内存所占的资源空间,不分离会产生僵尸线程。

分离属性的用法

例子2:分离属性的应用实例

建立thread_attr_detache.c;内容如下

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"

 //线程1
void *pthread_fun1(void *arg)
{
    printf("this is thread1 0x%ld\n",pthread_self());
    return (void*)1;
}
 
void *pthread_fun2(void *arg)
{
    printf("this is thread2 0x%ld\n",pthread_self());
    return (void*)2;
}
 
int main()
{

    pthread_t tid1,tid2;
    int errno1,errno2;
    int jval_1,jval_2;
    int *rval_1,*rval_2;
    pthread_attr_t attr;//声明属性对象
    pthread_attr_init(&attr);//初始化属性
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置属性
    
    errno1=pthread_create(&tid1,&attr,pthread_fun1,NULL);
    if(errno1 !=0)
    {
        printf("create new thread1 failure\n");
        return errno1;
     }
     
     errno2=pthread_create(&tid2,NULL,pthread_fun2,NULL);
     if(errno2 !=0)
     {
        printf("create new thread2 failure\n");
        return errno2;
     }
    
    jval_1=pthread_join(tid1,&rval_1);//等待新线程1执行完成1
    jval_2=pthread_join(tid2,&rval_2);//等待新线程2执行完成
 
    printf("jval_1=%d\n",jval_1);//连接线程1的函数返回值
    printf("jval_2=%d\n",jval_2);//连接线程2的函数返回值
    printf("rval_1=%d\n",(int*)rval_1);//线程1的返回值
    printf("rval_2=%d\n",(int*)rval_2);//线程2的返回值
    
   pthread_attr_destroy(&attr);     //销毁属性
    return 0;
}

当线程属性attr设置为分离属性DETACHED时,将无法与该线程进行连接,如例中将线程tread1的属性设置为分离属性;此时主线程就无法与之建立连接。而线程2的属性默认为NULL即非分离状态,运行结果如下图:

将属性设置为JOINABLE时,即非分离,且可以成功连接,运行结果如下图:

可以用pthread_attr_gettachstate()函数来获取其线程的分离状态;在主线程调用该函数。
3)栈属性
线程的栈大小与地址(查看栈的大小的命令:ulimit -s)

栈尾禁戒区

栈属性实例:获取和设置栈属性的大小;

建立thread_attr_setstack.c;内容如下

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"

pthread_attr_t attr;
void *pthread_fun(void *arg)
{
size_t stacksize;
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
        pthread_attr_getstacksize(&attr,&stacksize);
#endif 
    printf("this thread stack size is %ld\n",stacksize);
    return (void*)2;
}
 
int main()
{

    pthread_t tid;
    int errno;
    int jval;
    int *rval;
   
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
        pthread_attr_setstacksize(&attr,PTHREAD_STACK_MIN);
#endif
    
    errno=pthread_create(&tid,&attr,pthread_fun,NULL);
    if(errno !=0)
    {
        printf("create new thread1 failure\n");
        return errno;
     }
     
    
    jval=pthread_join(tid,&rval);//等待新线程执行完成
 
    printf("jval=%d\n",jval);//连接线程1的函数返回值
    printf("rval=%d\n",(int*)rval);//线程2的返回值
 
    pthread_attr_destroy(&attr);    
    return 0;
}


  • 如上代码通过pthread_attr_setstacksize设置栈大小为系统默认最小PTHREAD_STACK_MIN,运行结果如下:


如结果所示,默认最小值为16384

当我们不通过pthread_attr_setstacksize来设置其大小时,其默认值为最大值;我们也可以自己设置栈的大小,但不能小于其默认最小值,否则就会设置失败,设置失败时系统会自动默认设置为最大值;如我们设置为5000,修改如下:

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"

pthread_attr_t attr;
void *pthread_fun(void *arg)
{
size_t stacksize;
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
        pthread_attr_getstacksize(&attr,&stacksize);
        printf("first this thread stack size is %ld\n",stacksize);
        pthread_attr_setstacksize(&attr,5000);
#endif 
    printf("first this thread stack size is %ld\n",stacksize);
    return (void*)2;
}
 
int main()
{

    pthread_t tid;
    int errno;
    int jval;
    int *rval;
   
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
        //pthread_attr_setstacksize(&attr,PTHREAD_STACK_MIN);
#endif
    
    errno=pthread_create(&tid,&attr,pthread_fun,NULL);
    if(errno !=0)
    {
        printf("create new thread1 failure\n");
        return errno;
     }
     
    
    jval=pthread_join(tid,&rval);//等待新线程执行完成
 
    printf("jval=%d\n",jval);//连接线程1的函数返回值
    printf("rval=%d\n",(int*)rval);//线程2的返回值
 
    pthread_attr_destroy(&attr);    
    return 0;
}

其结果如下:

2,线程的同步属性
1)互斥量的属性

1,进程共享属性

2,类型属性

3,互斥量属性实例
将互斥量属性设置为进程共享属性,实现进程的同步;

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"
#include"signal.h"
#include"sys/shm.h"

int main()
{
    char *shm="myshm";
    char *shm1="myshm1";
    int shm_id,shm_id1;
    char *buf;
    pid_t pid;
    
    //打开共享内存
    shm_id=shmget(IPC_PRIVATE,128,0777);
    if(shm_id<0) 
        {
         printf("create shared memory failure\n");
         return -1;
        }
    //印射共享内存
    buf =(char*)shmat(shm_id,NULL,0);
    if(buf==NULL) 
        { 
        printf("reflect the shared memory failure\n"); 
        return -2;
         }

    pthread_mutex_t *mutex;
    pthread_mutexattr_t mutexattr;

     //打开共享内存 again
    shm_id1=shmget(IPC_PRIVATE,128,0777);
    if(shm_id1<0)
        {
         printf("second create shared memory failure \n");
         return -1;
        }
    //印射共享内存
    mutex =(char*)shmat(shm_id,NULL,0);
    if(mutex==NULL)
        {
        printf("reflect the shared memory to mutex failure\n");
        return -2;
        }
   pthread_mutexattr_init(&mutexattr);
#ifdef _POSIX_THREAD_PROCESS_SHARED
        pthread_mutexattr_setphared(&mutexattr,PTHREAD_PROCESS_SHARED);
#endif
        pthread_mutex_init(mutex,&mutexattr);

    pid=fork();
    if(pid==0)
    {
    //睡眠1S,认出cpu给父进程先运行
    sleep(1);
    printf("I'm child proccess\n");
    
     pthread_mutex_lock(mutex);
    //将共享内存的内存修改为hello
    memcpy(buf,"hello",6);
    printf("child buf is :%s\n",buf);
     pthread_mutex_unlock(mutex);
    }
    
    if(pid>0)
    {
    printf("I'm parent process\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);
    //解除印射
    shmdt(buf);
    //消除共享内存
    shmctl(shm_id,IPC_RMID,NULL);
     //
        shmdctl(shm_id1);
    return 0;
   }

   

2)读写锁的属性

3)条件变量的属性

4,线程的私有数据

5,线程和fork,如何在线程中安全的使用fork?
线程和fork

例子1:解铃还需系玲人

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"
#include"signal.h"
#include"sys/shm.h"
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

void *pthread_fun(void *arg)
{
    sleep(1);//睡眠认出CPU给主线程
    pid_t pid;
    pid=fork();//新线程里创建子进程
    if(pid==0)
    {
        //这里子进程里要锁住互斥量不成功,因为互斥量已经被主进程锁了
        //而且还未被主进程解锁。子进程是无法解锁的,子进程停留在这里
        //将不能往下执行。
        pthread_mutex_lock(&mutex);
        printf("child\n");
        pthread_mutex_unlock(&mutex);
    }
    if(pid>0)
    {   //当主线程睡眠2秒后,解锁完主进程就可以锁了。
        pthread_mutex_lock(&mutex);
        printf("parent\n");
        pthread_mutex_unlock(&mutex);
    }
 }
 
 int main()
 {
    pthread_t tid;
    int ret;
    ret=pthread_create(&tid,NULL,pthread_fun,NULL);
    if(ret!=0)
    {
        printf("create new thread failure\n");
        return -1;
    }
    //主线程里锁住互斥量
    pthread_mutex_lock(&mutex);
    sleep(2);//睡眠2秒,让新线程执行
    pthread_mutex_unlock(&mutex);
    printf("main\n");
    pthread_join(tid,NULL);
    
    return 0;
 }

运行结果如图:

由于fork的子进程是主进程的一个副本,副本里有主进程锁住的互斥量,子进程无法解锁,结果子进程里的“child”无法输出打印。
如果在fork()子进程前就解锁,这样子进程就可以lock和unlock了,结果如下。

再者在子线程里fork前加锁,如下:

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"
#include"signal.h"
#include"sys/shm.h"
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

void *pthread_fun(void *arg)
{
    sleep(1);//睡眠认出CPU给主线程
    pid_t pid;
    pthread_mutex_lock(&mutex);
    pid=fork();
    if(pid==0)
    {
        pthread_mutex_unlock(&mutex);
        printf("child\n");
    }
    if(pid>0)
    {
        pthread_mutex_unlock(&mutex);
        printf("parent\n");
        sleep(1);
    }
 }
 
 int main()
 {
    pthread_t tid;
    int ret;
    ret=pthread_create(&tid,NULL,pthread_fun,NULL);
    if(ret!=0)
    {
        printf("create new thread failure\n");
        return -1;
    }
    
    printf("main\n");
    pthread_join(tid,NULL);
    
    return 0;
 }

这样子线程在fork的时候就会把pthread_mutex_lock(&mutext)也fork进去了;
结果子进程就可以解锁了,结果如下:

例子:安全使用fork,调用atfork函数进行解锁;

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"
#include"signal.h"
#include"sys/shm.h"
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
void prepare()
{
        pthread_mutex_lock(&mutex);
        printf("I'm prepare\n");
}
void parent()
{
        pthread_mutex_unlock(&mutex);
        printf("I'm parent\n");
        sleep(1);
}
void child()
{
        pthread_mutex_unlock(&mutex);
        printf("I'm child\n");
}

void *pthread_fun(void *arg)
{
    sleep(1);//睡眠认出CPU给主线程
    pid_t pid;
    pthread_atfork(prepare,parent,child);
    pid=fork();
    if(pid==0)
    {
        pthread_mutex_lock(&mutex);
        printf("child process\n");
        pthread_mutex_unlock(&mutex);
    }
    if(pid>0)
    {
        pthread_mutex_lock(&mutex);
        printf("parent process\n");
        pthread_mutex_unlock(&mutex);
    }
 }
 
 int main()
 {
    pthread_t tid;
    int ret;
    ret=pthread_create(&tid,NULL,pthread_fun,NULL);
    if(ret!=0)
    {
        printf("create new thread failure\n");
        return -1;
    }
    
    pthread_mutex_lock(&mutex);
    sleep(2);
    pthread_mutex_unlock(&mutex);
    printf("main\n");
    pthread_join(tid,NULL);
    
    return 0;
 }

运行结果如下:

运行fork前调用prepare函数进行加锁,fork返回主进程前调用parent解锁,在fork返回子进程前调用child解锁。

最后,多线程总结

发布了50 篇原创文章 · 获赞 13 · 访问量 1828

猜你喜欢

转载自blog.csdn.net/weixin_38251305/article/details/104024152
今日推荐