嵌入式工作室第三期马拉松,多线程学习

嵌入式工作室第三期马拉松,多线程学习的日记,(⊙﹏⊙)对,日记,萌新瑟瑟发抖。

Posted by LXY on February 15, 2020

第三期马拉松,多线程

概念及理解

1、CPU

中央处理器(CPU,central processing unit)作为计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元。CPU 自产生以来,在逻辑结构、运行效率以及功能外延上取得了巨大发展。

在这里插入图片描述

在鸟哥的私房菜中有这样一个图,简明的展示了CPU的功能。

CPU组成核心部分
运算器:

(1)算术逻辑单元(ALU)。算加减乘的。

(2)中间寄存器(IR)。和栈密切相关。

(3)运算累加器(ACC)。

(4)描述字寄存器(DR)。存储描述字的。

(5)B寄存器。存地址的。

控制器:

​ 控制器由程序状态寄存器PSR,系统状态寄存器SSR, 程序计数器PG,指令均存器等组成。

​ 是决策机构。

分类
按照指令集分类

精简指令集计算机(RISC)(手机上的ARM架构) 复杂指令集计算机(CISC)(电脑x86架构)

嵌入式系统CPU

移动(Mobile)领域、实时(Real Time)嵌入式领域、深嵌入式领域

大型机CPU
我的CPU

当时买的时候呢考虑到预算的问题,i5和i7相比,i5就够了,只是效率慢了一些,所以就配了给i5。CPU参考天梯图就可以做到比较好的选择了。

2、核心

·核心(Core)又称为内核,是CPU最重要的组成部分。CPU中心那块隆起的芯片就是核心,是由单晶硅以一定的生产工艺制造出来的,CPU所有的计算、接受/存储命令、处理数据都由核心执行。各种CPU核心都具有固定的逻辑结构,一级缓存、二级缓存、执行单元、指令级单元和总线接口等逻辑单元都会有科学的布局。

·核心在某种程度上决定了CPU的性能。

多核心处理器

3、进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

定义

狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。

广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

特征

动态性

并发性

独立性

异步性

结构特征:进程由程序、数据和进程控制块三部分组成。

多个不同的进程可以包含相同的程序

内容

如果说处理器是一个课桌的话,那么进程就是一份作业,一个同学用这个课桌写完一份作业,然后把作业拿走,下一个同学再拿一份作业过来写,当然也可以写到一半就拿走去储存起来(切换)

状态

就绪状态(Ready)

运行状态(Running)

阻塞状态(Blocked)

4、线程

(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程。

同一进程中的多条线程将共享该进程中的全部系统资源。

一个进程可以有很多线程,每条线程并行执行不同的任务。

5、并行

在操作系统中是指,一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。对比地,并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)。

6、多线程

实现多个线程并发执行的技术

多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。

多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

线程的状态:创建 + 就绪 + 运行 + 阻塞(等待) + 退出

创建:一个新的线程被创建,等待该线程被调用执行; 就绪:时间片已用完,此线程被强制暂停,等待下一个属于他的时间片到来; 运行:此线程正在执行,正在占用CPU时间片; 阻塞:也叫等待状态,等待某一事件(如IO或另一个线程)执行完; 退出:一个线程完成任务或者其他终止条件发生,该线程终止进入退出状态,退出状态释放该线程所分配的资源。

7、超线程技术

超线程技术把多线程处理器内部的两个逻辑内核模拟成两个物理芯片,让单个处理器就能使用线程级的并行计算,进而兼容多线程操作系统和软件。超线程技术充分利用空闲CPU资源,在相同时间内完成更多工作。 [2]

虽然采用超线程技术能够同时执行两个线程,当两个线程同时需要某个资源时,其中一个线程必须让出资源暂时挂起,直到这些资源空闲以后才能继续。因此,超线程的性能并不等于两个CPU的性能。而且,超线程技术的CPU需要芯片组、操作系统和应用软件的支持,才能比较理想地发挥该项技术的优势。

我的理解是这样的:打个比方,两个同学(线程)在画画,他们用的同一盒水彩笔(核心),如果他们画画时,同时用不同的颜色,则可以同时画画,如果要同时用同一个颜色,那其中一个必须等另外一个用完后再用,相比以前只能一个人坐那里画画(单线程),效率就高一些。多线程技术是提供更多盒水彩笔,而超线程是一盒水彩笔能提供给更多的人使用。

8、守护进程

守护进程(daemon)是一类在后台运行的特殊进程,用于执行特定的系统任务。很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。另一些只在需要的时候才启动,完成任务后就自动结束。

守护进程是一个在后台运行并且不受任何终端控制的进程。Unix操作系统有很多典型的守护进程(其数目根据需要或20—50不等),它们在后台运行,执行不同的管理任务。

就是画画画到桌子底下去没人管他了。后台程序什么的,时钟应该算一种吧。

编程部分

线程唯独不能再windows下跑(vs2019实在配不出来环境),就用虚拟机吧。(jiba)后来我发现我大错特错了(三天后在我整了一顿虚拟机后回来写道)

可能会用到的命令

gcc-o [目标文件名] [待编译的文件名.c]

由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数:

于是乎变成了

gcc-o [目标文件名] -lpthread [待编译的文件名.c]

线程等待

进程中创建了个线程,不会等待他结束,可能线程没开始,进程就结束了,即:管生不管养。所以进程(main中)要等待线程进行

pthread_join(线程名,NULL);

我大概仿写了个这样的程序

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>


static int count = 1;
void fun(void* p);

int main(void)
{
	pthread_t pthread_1, pthread_2;//声明参数
	int test_pthread_1, test_pthread_2;//这个是用来判断线程创建是否成功,可改用bool

	test_pthread_1 = pthread_create(&pthread_1, NULL, (void*)&fun, NULL);
	test_pthread_2 = pthread_create(&pthread_2, NULL, (void*)&fun, NULL);
    //pthread_create()第四个函数可以给fun传参数呢,是个指针,意味着多个参数可以通过结构体struct传入!要求传入的是void*,但是可以强制转换啊 < _ <!
   
    // 线程创建成功,返回0,失败返回其他的 
	if (test_pthread_1 != 0) {
		printf("pthread_1 fail\n");
	}
	else {
		printf("pthread_1 success\n");
	}
	if (test_pthread_2 != 0) {
		printf("pthread_2 fail\n");
	}

	else {
		printf("pthread_1 success\n");
	}
	
    //线程测试
	pthread_join(pthread_1, NULL);
	pthread_join(pthread_2, NULL);
	return 0;
}

void fun(void* p) {
	int i;
	for (i = 1; i <= 8; i++) {
		printf("%d\n", i);
	}
}

然后虚拟机上运行一下

在这里插入图片描述

可能是芯片的原因我运行有好几次都没显示交错运行的情况,但是还是有几次有交错打印的情况,截屏如下:

在这里插入图片描述

但是这是什么意思啊(我后来懂了,是线程从创建就开始跑了,main也算线程,然后创建的线程就跑完了,才跑main,这些都是随机的,感谢明学长!QAQ)

在这里插入图片描述

改下代码

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>


static int count = 1;
void fun(void* p);

int main(void)
{
	pthread_t pthread_1, pthread_2;
	int test_pthread_1, test_pthread_2;

	test_pthread_1 = pthread_create(&pthread_1, NULL, (void*)&fun, "thread_1");
	test_pthread_2 = pthread_create(&pthread_2, NULL, (void*)&fun, "thread_2");
	// 线程创建成功,返回0,失败返回其他的 
	if (test_pthread_1 != 0) {
		printf("pthread_1 fail\n");
	}
	else {
		printf("pthread_1 success\n");
	}
	if (test_pthread_2 != 0) {
		printf("pthread_2 fail\n");
	}

	else {
		printf("pthread_2 success\n");
	}
	//线程测试
	pthread_join(pthread_1, NULL);
	pthread_join(pthread_2, NULL);
	return 0;
}

void fun(void* p) {
	int i;
	char* name = (char*)p;
	for (i = 1; i <= 8; i++) {
		printf("%s: %d\n", name, i);
	}
}

在这里插入图片描述在这里插入图片描述

接下来我要讲一个鬼故事。我花了一天配置vim,然后curl老报错(我想把vim集成成IDE),一怒回档了虚拟机。然后神奇是事情发生了。同样的代码他跑出来是酱紫的。

在这里插入图片描述

然而另我更大跌眼镜的事情是,我发现,windows下,dev c++可以编译线程??而且完全不用配置环境???我懂了,遇事不决dev,dev c++真好东西!简直鬼故事。然后我加了给Sleep函数规避上面提到的问题,竟然用dev打出了更好的结果。用了sleep之后,能明显感觉出交错打印的感觉。顺便解释一下同时运行。微观上讲,计算机不能同时运行多个进程,而是在两个不同的进程中不断切换,由于计算机完成一次运算的时间非常短,所有看上去就像同时运行一样。但是频繁切换会导致切换所做的时间占用过多,影响效率,这就解释了为什么同时复制粘贴两个文件还没有依次复制快。

在这里插入图片描述

//还是放个代码在这里(和前面有些重复了,哎,日记嘛)

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <windows.h>


static int count = 1;
void fun(void* p);

int main(void)
{
	pthread_t pthread_1, pthread_2;
	int test_pthread_1, test_pthread_2;

	test_pthread_1 = pthread_create(&pthread_1, NULL, (void*)&fun, "thread_1");
	test_pthread_2 = pthread_create(&pthread_2, NULL, (void*)&fun, "thread_2");
	// 线程创建成功,返回0,失败返回其他的 
	if (test_pthread_1 != 0) {
		printf("pthread_1 fail\n");
	}
	else {
		printf("pthread_1 success\n");
	}
	if (test_pthread_2 != 0) {
		printf("pthread_2 fail\n");
	}

	else {
		printf("pthread_2 success\n");
	}
	//线程测试
	pthread_join(pthread_1, NULL);
	pthread_join(pthread_2, NULL);
	return 0;
}

void fun(void* p) {
	Sleep(200);
	int i;
	char* name = (char*)p;
	for (i = 1; i <= 8; i++) {
		printf("%s: %d\n", name, i);
		Sleep (100);
	}
}

线程资源争夺(race condition),锁🔒(lock)

#include <stdio.h>

#include <stdlib.h>

#include <Windows.h>

#include <pthread.h>

#include <time.h>


int sum = 0;

void* func(void* args);

int main(void)
{
	pthread_t th1;
	pthread_t th2;

	pthread_create(&th1, NULL, func(NULL), NULL);
    //devc++这里很奇怪的要在func带参数
	pthread_create(&th2, NULL, func(NULL), NULL);

	printf("sum = %d\n", sum);
	system("pause");
	
	return 0;

}

void* func(void* atgs) 
{
	int i = 0;
	for (i = 0; i < 100000; i++)
	{
		sum++;
	}
	
}


这就是个例子:几个线程争夺同一个文件。

我的电脑cpu可能比较牛逼,他怎么跑都没有争夺,就不放结果了。

防止资源争夺的办法是加锁🔒。锁我理解为一个权限 ,谁先抢到他,谁就有权继续运行下去。就是说,我摆一个🔒在那里,几个线程去抢他,抢到的就先运行,没抢到的就无法运行,要等拿到🔒的线程把🔒还回来才能抢到。

声明锁pthread_mutex_t

初始化锁pthread_mutex(&lock,NULL)

加锁pthread_mutex_lock(&lock)

解锁pthread_mutex_unlock(&lock)

#include <stdio.h>

#include <stdlib.h>

#include <Windows.h>

#include <pthread.h>

#include <time.h>


pthread_mutex_t lock;//声明锁

int sum = 0;

void* func(void* args);

int main(void)
{
	pthread_t th1;
	pthread_t th2;
	pthread_mutex_init(&lock,NULL);//初始化锁

	pthread_create(&th1, NULL, func(NULL), NULL);
	pthread_create(&th2, NULL, func(NULL), NULL);

	printf("sum = %d\n", sum);
	system("pause");
	
	return 0;

}

void* func(void* atgs) 
{

	int i = 0;
	for (i = 0; i < 100000; i++)
	{
		pthread_mutex_lock(&lock);//加锁
		sum++;
		pthread_mutex_unlock(&lock);//开锁
	}
	
}