POSIX线程库(二)线程同步轮询技术——一种笨笨方法

一、计算机内存布局

  • 文本区(程序代码区):这是二进制代码的存储位置,主要包含程序的执行指令。为防止数据被意外修改,此区域通常被设置为只读。

  • 数据区:这是全局变量和静态变量的存储位置,可进一步分为已初始化数据区(.DATA)和未初始化数据区(.BSS)。

    • 已初始化数据区(.DATA):如果全局变量或静态变量在声明时被赋予了初值,那么它们会被存放在此区域。
    • 未初始化数据区(.BSS):BSS(Block Started by Symbol)是指在程序开始运行之前,计算机会把此区域的数据全部设置为0。如果我们在C++中声明了静态变量或全局变量但没有初始化,那么编译器将会将这些变量标记为BSS区的数据,程序运行时,这些变量会被自动初始化为0。
  • 堆。堆是进程的一部分,它是程序运行时动态分配内存的区域。一个进程启动时,操作系统将会为其分配一个私有的虚拟内存空间,同一个进程的所有线程都会看见并使用这块空间。

  • 文件等共用资源。

【独享的资源有】

  • 栈 栈是独享的

  • 寄存器 这个可能会误解,因为电脑的寄存器是物理的,每个线程去取值难道不一样吗?其实线程里存放的是副本,包括程序计数器PC

线程共享的环境包括:进程代码段、进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。[1]

为什么线程常常和“不安全”在一起?

  • 原子性:一个或者多个操作在 CPU 执行的过程中被中断
  • 可见性:一个线程对共享变量的修改,另外一个线程不能立刻看到
  • 有序性:程序执行的顺序没有按照代码的先后顺序执行[2]

张三及其女友约好共同做好这一道"红烧排骨",男友负责食材处理、放调料、开火,女友负责收汁装盘。

  • 原子性,张三正在执行放盐操作45%,电话响了,张三放下了手中的勺子接电话,因为张三放盐操作执行了45%,因为无法评估放盐执行动作,放盐操作是“原子性”的,只能是放盐和成功和失败,于是一份不合格料理出来了“偏咸或偏淡”的红烧排骨,于是两人分手;
  • 可见性,本着时间利用的原则,女朋友在男朋友准备食材、开火和放调料时,决定去看看电视剧,男朋友完成其工作后,没有告知的情况下取快递了,于是到饭点了,女朋友认为男友尚未准备好食材,于是两人吃了白米饭后分手了;
  • 有序性,这道菜显然是由先后顺序的,女朋友在男友尚未完成其工序的时候,直接收汁装盘,男朋友看着摆在桌子前生的排骨,决定分手。

在这个例子上,两个人一起做“红烧排骨”这道菜,就是多线程任务,由于做饭的原子性、可见性和有序性的要求,会直接影响这道菜的好坏进而导致分手还是牵手,所以说一起做菜是“不安全”的,容易分手。

什么是轮询?

轮询就是女朋友一直在查询男朋友有没有做完他的工作,做完了就开始完成自己的任务了,这个方法的缺点是,女朋友无法看剧。

下面展示这个程序,完成了A B协调,A打印后B打印。设置了一个全局变量value,value更像是一种标记,当对方线程尚未完成其操作时将会等待,当对方线程完成就会让对方等待,自己执行。

程序

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

char message[]="Hello Thread World";

int value=1;
int count1=0;
void *thread1(void *arg)
{
	//int count=0;
	while(count1++<20)
	{
		if(value==1)
		{
			value=2;
			printf("%d",value);
		}	
		else
		{
			sleep(1);
		}
	}

	pthread_exit(NULL);
}

int count2=0;

void *thread2(void *arg)
{
	//int count=0;
	while(count2++<20)
	{
		if(value==2)
		{
			value=1;
			printf("%d",value);
		}	
		else
		{
			sleep(1);
		}
		
	}

	pthread_exit(NULL);
}

int main()
{
	int res;
	pthread_t th1,th2;
	void *thread_result;

	res=pthread_create(&th1,NULL,thread1,(char *)message);
	if(res!=0)
	{
		perror("thread1 creation failed");
		exit(EXIT_FAILURE);
	}
	res=pthread_create(&th2,NULL,thread2,(char *)message);

	if(res!=0)
	{
		perror("thread2 creation failed");
		exit(EXIT_FAILURE);
	}
	
	
	res=pthread_join(th1,NULL);
	if(res!=0)
	{
		perror("Thread join failed");
		exit(EXIT_FAILURE);
	}
	res=pthread_join(th2,NULL);
	if(res!=0)
	{
		perror("Thread join failed");
		exit(EXIT_FAILURE);
	}

	exit(EXIT_SUCCESS);

}

小结

这种简单的通过标志位+经验延时来完成的同步,加延时的目的是保证线程不在同一时间访问同一个普通变量value,因为普通变量不具有原子性,只能通过时间保证变量已经稳定(盐放完了)。下一篇文章,将会讲到一种特殊的变量——信号量,他只需要等待有限、固定的时长(等待信号锁资源释放)就可以完成线程之间的同步。

[1] 操作系统–多线程之间共享哪些资源?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值