python20-线程和进程基础

一、为什么要有操作系统

关于操作系统的定义有很多,就随便找几个看看:

定义1:操作系统是一个用来协调、管理和控制计算机硬件和软件资源的系统程序,它位于硬件和应用程序之间

定义2:操作系统的内核是一个管理和控制程序,负责管理计算机的所有物理资源,其中包括:文件系统、内存管理、设备管理和进程管理

二、操作系统历史

2.1真空管与穿孔卡片(无操作系统)

一代计算机

过程:
程序员们将对应于程序和数据的已穿孔的纸带(或卡片)装入输入机,然后启动输入机把程序和数据输入计算机内存,接着通过控制台开关启动程序针对数据运行;计算完毕,打印机输出计算结果;用户取走结果并卸下纸带(或
卡片)后,才让下一个用户上机。

注意点:
程序员需要在墙上的计时表上预约时间 
同一时刻只有一个程序在内存中被CPU调用运行(串行的)

优点:程序员在申请的时间段内独享整个资源,即时的调试自己的程序,如果有bug可以当场处理,
缺点:这对于计算机提供商来成本太高
View Code

2.2晶体管和批处理系统

一代计算机存在的问题:
人机交互太多了(输入--->计算--->输出  输入--->计算--->输出 输入--->计算--->输出 )

解决办法:
把一堆人的输入攒成一大波输入,然后顺序计算(这是有问题的,但是第二代计算没有解决)再把计算结果攒成一大波输出,这就是批处理系统

在收集了大约一个小时的批量作业之后,这些卡片被读入磁带,然后磁带被送到机房里并装到磁带上。然后磁带被送到机房里并装到磁带机上。随后,操作员装入一个特殊的程序(此乃现代操作系统的前身),它负责从磁带上读入第一个作业(job,一个或一组程序)并运行,其输出写到第二个磁带上,而且不打印。每个作业结束后,操作系统自动的从磁带上读入下一个作业并且运行。当一整批的作业全部结束后,操作员去下输入和输出磁带,讲输入磁带换成下一批作业,并且把输出磁带拿到一台1041机器上进行脱机(不与主计算机联机)打印,这就是操作系统的前身。

优点:批处理
缺点: 1 图的中间还有俩小人  2 仍然是顺序计算
View Code

2.3集成电路芯片和多道程序设计

第三代计算机

针对二代计算机的两个主要问题,开发出SPOOLING技术

卡片被拿到机房后能够很快的将作业从卡片读入磁盘,于是任何时刻当一个作业结束时,操作系统就能将一个作业从磁带读出,装进空出啦的内存区域运行,这种技

术叫做同时的外部设备联机操作:SPOOLING该技术同时用于输出。当采用了这种技术后,就不在需要IBM1401机了,也不必将磁带搬来搬去了(中间俩小人失业了),

强化了操作系统的功能开发出多道程序设计,用于解决顺序执行的问题:

新的问题:在7094机上(程序运行的机器),若当前作业因等待磁带或等待其他IO操作而暂停,CPU就处于休闲状态直至IO操作完成。对于CPU密集的科学计算,IO操作少,浪费的时间不明显;对于商业数据处理,IO等待能到达80%~90%,所以必须解决CPU浪费的现象。

解决方案:将内存分为几个部分,每一部分存放不同的作业,如上图所示。当一个作业等待IO完成时,另一个作业可以使用CPU,内存中放足够的作业,则CPU的利用率能接近100%
View Code

多道技术是为了提高CPU的效率

第四代计算机

分时操作系统:多个联机终端+多道技术

此时的第三代计算机适合大型科学计算和繁忙的商务数据处理,但,本质上其仍是一个批处理系统。
虽然解决了诸如以上问题,但多个作业必须在全部运行结束后,才能得到结果,从一个作业的提交到运算结果取回往往长达数小时。
想象一个场景:A君 B君 C君 三个程序员同时在调试程序,一旦A君写错一个逗号,那么可能需要半天的时间才能看到结果,因为B君C君的结果也同时运算出来了。时间必然要长。一言以蔽之:大家一起存作业,大家一起去数据(磁带)

许多程序员怀念第一代独享的计算机,可以即时调试自己的程序。为了满足程序员们很快可以得到响应,出现了分时操作系统

20个客户端同时加载到内存,有17在思考,3个在运行,cpu就采用多道的方式处理内存中的这3个程序,由于客户提交的一般都是简短的指令而且很少有耗时长的,索引计算机能够为许多用户提供快速的交互式服务,所有的用户都以为自己独享了计算机资源。
View Code

2.4个人计算机

随着大规模集成电路的发展,每平方厘米的硅片芯片上可以集成数千个晶体管,个人计算机的时代就此到来

三、进程

假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作),而此时CPU只能静静地等待任务A读取完数据才能继续执行,这样就白白浪费了CPU资源。是不是在程序A读取数据的过程中,让程序B去执行,当程序A读取完数据之后,让程序B暂停,然后让程序A继续执行?当然没问题,但这里有一个关键词:切换。

既然是切换,那么这就涉及到了状态的保存,状态的恢复,加上程序A与程序B所需要的系统资源(内存,硬盘,键盘等等)是不一样的。自然而然的就需要有一个东西去记录程序A和程序B分别需要什么资源,怎样去识别程序A和程序B等等,所以就有了一个叫进程的抽象概念。
View Code
定义:
进程就是一个程序在一个数据集上的一次动态执行过程。

组成:
由程序、数据集、进程控制块三部分组成。

程序:用来描述进程要完成哪些功能以及如何完成,简单点就是代码;
数据集:则是程序在执行过程中所需要使用的资源;
进程控制块:用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志
举个例子:
想象一位有一手好厨艺的计算机科学家正在为他的女儿烘制生日蛋糕。他有做生日蛋糕的食谱,厨房里有所需的原料:面粉、鸡蛋、糖、香草汁等。在这个比喻中,做

蛋糕的食谱就是程序(即用适当形式描述的算法)计算机科学家就是处理器(cpu),而做蛋糕的各种原料就是输入数据。进程就是厨师阅读食谱、取来各种原料以及烘制

蛋糕等一系列动作的总和。现在假设计算机科学家的儿子哭着跑了进来,说他的头被一只蜜蜂蛰了。计算机科学家就记录下他照着食谱做到哪儿了(保存进程的当前状

态),然后拿出一本急救手册,按照其中的指示处理蛰伤。这里,我们看到处理机从一个进程(做蛋糕)切换到另一个高优先级的进程(实施医疗救治),每个进程拥有各

自的程序(食谱和急救手册)。当蜜蜂蛰伤处理完之后,这位计算机科学家又回来做蛋糕,从他离开时的那一步继续做下去。

切换的情形有两种:

1.遇到IO就切换

2.时间轮询

五、线程

线程的出现是为了降低上下文切换的消耗,提高系统的并发性,并突破一个进程只能干一样事的缺陷,使到进程内并发成为可能。

假设,一个文本程序,需要接受键盘输入,将内容显示在屏幕上,还需要保存信息到硬盘中。
若只有一个进程,势必造成同一时间只能干一样事的尴尬(当保存时,就不能通过键盘输入内容)。
若有多个进程,每个进程负责一个任务,进程A负责接收键盘输入的任务,进程B负责将内容显示在屏幕上的任务,进程C负责保存内容到硬盘中的任务。
这里进程A,B,C间的协作涉及到了进程通信问题,而且有共同都需要拥有的东西-------文本内容,不停的切换造成性能上的损失。
若有一种机制,可以使任务A,B,C共享资源,这样上下文切换所需要保存和恢复的内容就少了,同时又可以减少通信所带来的性能损耗,那就好了,而这种机制就是线程。

线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序
计数器、寄存器集合和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能。线程没有自己的系统资源。

为什么要有线程?

1.进程开启都是独占一份内存

2.一个进程里面的线程是共享这个进程里面的内存

3.线程的切换要比进程的切换花销少

线程与进程之间的区别

1 一个程序至少有一个进程,一个进程至少有一个线程(进程可以理解成线程的容器)

2 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

3 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和
  程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 

4 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调
  度的一个独立单位. 

5 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程
  自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 
  一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

切换前保存的状态不是放在内存里面的,而是临时放在寄存器里面的。又因为寄存器和CPU是邻居,所以cpu就不用跑到内存去读取“进程的状态”,大大提高了切换的效率。

六、多线程

基本多线程(常用)

targrt=函数名  args=(参数1,参数2.....)

import threading #线程
import time

def Hi(num):
    print("hello %d"%num)
    time.sleep(3)

if __name__=="__main__":
    t1=threading.Thread(target=Hi,args=(10,)) #创建了一个线程对象t1
    t1.start() #开启线程t1

    t2=threading.Thread(target=Hi,args=(11,))
    t2.start()

    print("ending...")

#整个程序就是一个主线程
View Code

自定义多线程

必须重写run方法,一般很少用

import threading
import time

#继承式调用
class Mythread(threading.Thread):
    def __init__(self,num):
        threading.Thread.__init__(self)
        self.num=num

    def run(self):
        print("running on number%s" %self.num)

        time.sleep(3)

if __name__ == "__main__":
    t1=Mythread(1)
    t2=Mythread(2)
    t1.start()
    t2.start()

    print("ending...")
View Code

join方法

要求主线程在子线程执行完毕之后再执行

#正常情况下主线程的代码和子线程的代码一起执行,并且在所有子线程执行完毕后结束整个程序
# import threading
# import time
#
# def music():
#     print("begin to listen %s"%time.ctime())
#     time.sleep(3)
#     print("stop to listen %s" % time.ctime())
#
# def game():
#     print("begin to play game %s" %time.ctime())
#     time.sleep(5)
#     print("stop to play game %s" %time.ctime())
#
# if __name__ =="__main__":
#     t1=threading.Thread(target=music)
#     t2=threading.Thread(target=game)
#
#     t1.start()
#     t2.start()
#
#     print("ending...")


#情况一:join在外面
# import threading
# import time
#
# def ListenMusic(name):
#     print("Begin listening to %s.%s" %(name,time.ctime()))
#     time.sleep(3)
#     print("end listening %s"%time.ctime())
#
# def RecordBlog(title):
#     print("Begin recording the %s! %s"%(title,time.ctime()))
#     time.sleep(5)
#     print("end recording %s"%time.ctime())
#
# threads=[]
#
# t1 = threading.Thread(target=ListenMusic,args=("Newyork",))
# t2 = threading.Thread(target=RecordBlog,args=("python",))
#
# threads.append(t1)
# threads.append(t2)
#
# if __name__ == "__main__":
#     for t in threads:
#         t.start()
#         t.join() #join放到这里面,t1开启了,一join,t2就要等t1执行完才能执行,如此类推......就跟串行一样
#
#     print("all over %s" %time.ctime())

# 情况二:join在里面
# 主线程等t1、t2打印ending后,再执行自己的代码并结束整个程序
# import threading
# import time
#
# def ListenMusic(name):
#     print("Begin listening to %s.%s" %(name,time.ctime()))
#     time.sleep(3)
#     print("end listening %s"%time.ctime())
#
# def RecordBlog(title):
#     print("Begin recording the %s! %s"%(title,time.ctime()))
#     time.sleep(5)
#     print("end recording %s"%time.ctime())
#
# threads=[]
#
# t1 = threading.Thread(target=ListenMusic,args=("Newyork",))
# t2 = threading.Thread(target=RecordBlog,args=("python",))
#
# threads.append(t1)
# threads.append(t2)
#
# if __name__ == "__main__":
#     for t in threads:
#         t.start()
#     t.join()
#
#     print("all over %s" %time.ctime())

# 情况三:其中一个子线程join
# import threading
# import time
#
# def ListenMusic(name):
#     print("Begin listening to %s.%s" %(name,time.ctime()))
#     time.sleep(3)
#     print("end listening %s"%time.ctime())
#
# def RecordBlog(title):
#     print("Begin recording the %s! %s"%(title,time.ctime()))
#     time.sleep(5)
#     print("end recording %s"%time.ctime())
#
# threads=[]
#
# t1 = threading.Thread(target=ListenMusic,args=("Newyork",))
# t2 = threading.Thread(target=RecordBlog,args=("python",))
#
# threads.append(t1)
# threads.append(t2)
#
# if __name__ == "__main__":
#     for t in threads:
#         t.start()
#     #t2.join() #t1、t2都执行完毕后(因为t2执行时间长,这个时间t1也执行完了),主线程执行自己的代码,然后结束整个程序
#     t1.join() #等t1执行结束后,主线程再执行,然后最后打印t2的end
#     #这种情况要看两个子线程的执行时间
#     print("all over %s" %time.ctime())
View Code

疑问:for循环外面的t.join为什么会生效?它都不在for循环的作用域里面

答:有作用域的对象是类、函数、模块,而for循环、if...else是没有作用域的

set_Daemon方法

被设置为守护线程的子线程,会随着主线程的结束而结束,不管该子线程是否执行完毕

# setDaemon一定要写在start之前
import threading
import time

def ListenMusic(name):
    print("Begin listening to %s.%s" %(name,time.ctime()))
    time.sleep(3)
    print("end listening %s"%time.ctime())

def RecordBlog(title):
    print("Begin recording the %s! %s"%(title,time.ctime()))
    time.sleep(5)
    print("end recording %s"%time.ctime())


threads=[]
t1 = threading.Thread(target=ListenMusic,args=("Newyork",))
t2 = threading.Thread(target=RecordBlog,args=("python",))

threads.append(t1)
threads.append(t2)

if __name__ == "__main__":
    #t1.setDaemon(True) #主线程不管t1的死活,只要t2执行完整个程序就结束,因为t1的执行时间比t2短,所以还看不出效果
    t2.setDaemon(True) #主线程不管t2的死活,只要t1执行完整个程序就结束,结果t2没打印最后一句
    for t in threads:
        #t.setDaemon(True) #t1、t2打印完第一句以及主线程执行完程序就结束了
        t.start()

    print("all over %s" %time.ctime())
View Code

多线程实例的其他方法

setName:设置线程的名字

getName:获取线程的名字

isAlived:新城是否活动

threading类的方法

threading.active_count(),可通过该方法实现jion效果,加一个判断“当活跃的线程等于一个的时候”,就意味着子线程都执行完毕了,那么主线程就可以执行其代码了

while threading.active_count()==1:
    print ("all over %s" %ctime())
View Code

猜你喜欢

转载自www.cnblogs.com/liangjiongyao/p/8884499.html