Linux——线程安全

线程安全

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

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

由于线程是并发运行的,并且一个进程中的线程之间共享地址空间、如果这个多线程的程序运行过程中存在多线程去修改共享的数据时,会造成执行结果的二义性。

多线程修改共享的数据

——使用线程同步(互斥锁)来解决
示例代码

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

#include<pthread.h>

int gdata = 1;//.data段保存的数据

void *fun(void *arg)
{
	int i = 0;//fun函数的栈区定义的变量
	for(;i < 10000;i++)
	{
		gdata++;
	}
}

int main()
{
	pthread_t id;
	int res = pthread_create(&id,NULL,fun,NULL);
	assert(res == 0);

	//下面的代码由主线程和函数线程并发执行
	int i = 0;//main函数栈区定义的变量
	for(;i < 10000;i++)
	{
		gdata++;//1.读取内存当前的值2.在寄存器上执行++操作3.写回内存
	}

	pthread_join(id,NULL);

	//以下的代码只有main线程
	printf("gdata = %d\n",gdata);
	exit(0);
}


在这里插入图片描述
由以上执行结果可以看出:同一份代码,相同的初始值,几次结果不相同。

可重入函数

引言

函数是一段载入到内存的代码。函数的代码可长可短,执行时间长度也不确定。在多线程中,线程之间是可以进行切换的。函数是一段写好的代码,属于程序公有的代码段。一个进程中有多个线程,每一个线程都可以调用这段函数代码执行。而在多线程环境中,线程的切换是无法预料的,你不知道下一秒是哪个线程在执行,每时每刻的运行环境都不一样,因为线程切换也是变化莫测的。这是操作系统调度进程线程的范围,不是我们能够掌控的。既然我们无法改变进程调度,无法得知线程切换的规律,那就不要依赖线程的切换。我们的程序要做到,无论线程怎么切换,执行的结果都要一致。而我们该如何才能够达到无论线程如何切换,执行结果都是一致的呢?

重入:重复进入!!!!
一个函数在执行的过程中被打断,然后会再被再重头执行一次,执行完后,再回来把刚才没执行完的部分执行完。这就相当于嵌套的执行了。函数是公共代码,这样的执行是允许的。函数的执行可以被打断,打断之后还可以再重头执行,执行完后接着执行刚才没有执行的代码,然后第一次执行的代码(被打断的函数)执行结果还是正确的。也就是说,这个函数执行,无论中间把这个函数再嵌入执行多少遍,怎么嵌入,最终执行完,执行的结果都是正确的,这样的函数就是可重入函数

如果一个函数在同一时刻可以被多个线程安全地调用,就称该函数是线程安全的,除了下表列出的函数以外,其他函数都保证是线程安全的。

不保证线程安全的常见函数在这里插入图片描述

strtok()是不保证线程安全的,在多线程程序中,就可能会出现问题,strtok()实现的代码使用了线程间共享的数据。
示例代码

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

#include <pthread.h>

void *fun(void *arg) 
{    
	char buff[] = "1 2 3 4 5 6 7 8"; // fun的栈区   
	//char *p = strtok(buff, " ");  // 不保证线程安全    
	char *q = NULL;    
	char *p = strtok_r(buff, " ", &q);    
	while(p != NULL)    
	{        
		printf("fun : %s\n", p);   
		// p = strtok(NULL, " ");  // 不保证线程安全        
		p = strtok_r(NULL, " ", &q);         
		sleep(1);    
	} 
}
int main() 
{    
	pthread_t id;    
	int res = pthread_create(&id, NULL, fun, NULL);    
	assert(res == 0);
	
    //  并发执行的代码    
    char buff[] = "a b c d e f g h"; // main线程的栈区    
    //char *p = strtok(buff, " ");   // 不保证线程安全    
    char *q = NULL;    
    char *p = strtok_r(buff, " ", &q);    
    while(p != NULL)    
    {        
    	printf("main: %s\n", p);        
    	//p = strtok(NULL, " ");   // 不保证线程安全        
    	p = strtok_r(NULL, " ", &q);        
    	sleep(1);    
    }
    pthread_join(id, NULL);    
    exit(0);
 }

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值