Linux20 -- 线程安全、保证线程安全的示例代码、线程与 fork

一、线程安全

线程安全即就是在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是一样的、正确的。那么就说这些线程是安全的。

线程安全:多线程程序,无论调度顺序咋样,都可以得到正确一致的结果。安全–正确性。
同步,线程安全的函数/可重入函数。

要保证线程安全需要做到:
1) 对线程同步,保证同一时刻只有一个线程访问临界资源。
2)在多线程中使用线程安全的函数(可重入函数),所谓线程安全的函数指的是:如果一个函数能被多个线程同时调用且不发生竟态条件,则我们程它是线程安全的。

二、保证线程安全的示例代码

1、strtok函数的线程调用示例,不保证线程安全:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

void* fun(void *arg)
{
    char buff[]={"a b c d e f g h j"};
    char *s = strtok(buff," ");
    while(s != NULL)
    {
        printf("fun s = %s\n",s);
        sleep(1);
        s = strtok(NULL," ");
    }
}

int main()
{
    pthread_t id;
    pthread_create(&id,NULL,fun,NULL);

    char arr[]="1 2 3 4 5 6 7 8 9";
    char *s = strtok(arr," ");
    while(s != NULL)
    {
        printf("main s = %s\n",s);
        sleep(1);
        s = strtok(NULL," ");
    }

    pthread_join(id,NULL);
    
}

运行结果:
在这里插入图片描述

2、strtok_r函数的线程调用

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

void* fun(void *arg)
{
    char buff[]={"a b c d e f g h j"};
    char * ptr = NULL;
    char *s = strtok_r(buff," ",&ptr);
    while(s != NULL)
    {
        printf("fun s = %s\n",s);
        sleep(1);
        s = strtok_r(NULL," ",&ptr);
    }
}

int main()
{
    pthread_t id;
    pthread_create(&id,NULL,fun,NULL);

    char arr[]="1 2 3 4 5 6 7 8 9";
    char *ptr = NULL;
    char *s = strtok_r(arr," ",&ptr);
    while(s != NULL)
    {
        printf("main s = %s\n",s);
        sleep(1);
        s = strtok_r(NULL," ",&ptr);
    }

    pthread_join(id,NULL);
    
}

运行结果:

在这里插入图片描述

三、线程与 fork

1、多线程中某个线程调用 fork(),子进程会有和父进程相同数量的线程吗?

ps -ef
ps -eLf --查看线程id

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

void* fun(void *arg)
{
    for(int i = 0;i < 5;i++)
    {
        printf("fun run , pid = %d\n",getpid());
        sleep(1);
    }
}

int main()
{
    pthread_t id;
    pthread_create(&id,NULL,fun,NULL);

    for( int i = 0; i < 5 ; i++)
    {
        printf("main run , pid = %d\n",getpid());
        sleep(1);
    }

    pthread_join(id,NULL);
    exit(0);
}

运行结果:

在这里插入图片描述

原理:

Linux实现线程的机制非常独特。从内核的角度来说,它并没有线程这个概念。Linux把所有的线程都当做进程来实现。内核并没有准备特别的调度算法或是定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都拥有唯一隶属于自己的 task_struct,所以在内核中,它看起来就像是一个普通的进程(只是线程和其他一些进程共享某些资源,如地址空间)。

2、父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁?

fork会将锁复制,但为保证安全,会将锁默认锁上。
为防止出现错误。父子进程在使用锁的时候,可以先对其进行解锁,再使用锁。

解决方法:pthread_atfork()

test3.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>


pthread_mutex_t mutex;

void* fun(void *arg)
{
    pthread_mutex_lock(&mutex);
    printf("fun lock\n");
    sleep(5);
    pthread_mutex_unlock(&mutex);
    printf("fun unlock\n");
}

int main()
{
    pthread_t id;
    pthread_mutex_init(&mutex,NULL);//对锁进行初始化
    pthread_create(&id,NULL,fun,NULL);
    
    sleep(1);//等待线程运行,等其进行加锁
    pid_t pid = fork(); 
    if(pid == -1)
    {
        exit(0);
    }

    if(pid == 0)
    {
        printf("子进程将要加锁\n");
        pthread_mutex_lock(&mutex);
        printf("子进程加锁成功\n");
        pthread_mutex_unlock(&mutex);
    }
    else
    {
        wait(NULL);//子进程未结束,会阻塞
        printf("main over\n");
    }

    pthread_join(id,NULL);
    exit(0);
}

test4.c //使用pthread_atfork

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>


pthread_mutex_t mutex;

void* fun(void *arg)
{
    pthread_mutex_lock(&mutex);
    printf("fun lock\n");
    sleep(5);
    pthread_mutex_unlock(&mutex);
    printf("fun unlock\n");
}

void at_lock()
{
    pthread_mutex_lock(&mutex);
}

void at_unlock()
{
    pthread_mutex_unlock(&mutex);
}

int main()
{
    pthread_t id;
    pthread_atfork(at_lock,at_unlock,at_unlock);
    pthread_mutex_init(&mutex,NULL);//对锁进行初始化
    pthread_create(&id,NULL,fun,NULL);
    
    sleep(1);//等待线程运行,等其进行加锁
    pid_t pid = fork(); 
    if(pid == -1)
    {
        exit(0);
    }

    if(pid == 0)
    {
        printf("子进程将要加锁\n");
        pthread_mutex_lock(&mutex);
        printf("子进程加锁成功\n");
        pthread_mutex_unlock(&mutex);
    }
    else
    {
        wait(NULL);//子进程未结束,会阻塞
        printf("main over\n");
    }

    pthread_join(id,NULL);
    exit(0);
}

运行结果:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值