在学习Python的过程中,一种最直接的感触就是上手轻松,但精通很难,很多时候离不开计算机学科的基础知识。比如下面这个问题,我是没法第一时间想到准确答案的。
- 协程和线程有什么区别?
在学习的道路上,看懂听懂是输入,写明说清是输出,只有达到输出这一步才算真正搞懂一个知识点。So,今天就先搞搞这个问题,用海贼王的故事来理解个中差异。
协程、线程,我再加个进程。
1 进程
进程(process)是一个执行中的程序。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。操作系统管理其上所有进程的执行,并为这些进程合理地分配时间。因为每个进程都拥有自己的内存和数据栈等,所以只能采用进程间通信(IPC)的方式共享信息。
进程,直观点说,保存在硬盘上的程序运行以后,会在内存空间里形成一个独立的内存体,这个内存体有自己独立的地址空间,有自己的堆,上级挂靠单位是操作系统。操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位。
OK,下面就到了海贼show time。
话说路飞一行人从新手村开始,斩世界政府旗帜,闯海军本部,灭七武海,挑四皇,一直打怪升级,练就一身本领,霸气外露。如今来到了和之国,正和小伙伴一起讨伐凯多。这个时候,讨伐凯多(和之国篇)就是海贼王世界中的一个进程,它的地址空间是凯多大本营——和之国。 那要如何才能灭了凯多呢?讨伐大军需要先混入敌军地盘,接着潜伏收集情报,然后密谋计划,最后杀入敌营斩下凯多首级,这些属于整个讨伐进程的子进程。讨伐大军是怎么获取的情报,敌人老窝是怎么潜入的,路飞是怎么打的,佐罗是怎么砍的。。。这些就属于进程代码段的内容。凯多也不是吃素的,起飞鬼岛砸向花之都,鬼岛的飞行时间应该就是整个剧情高潮时间,是一个全局变量,属于进程数据段内容。这边草帽一伙打得火热,世界政府、海军、革命军也没闲着,虽然作者没有交代,但也在海贼世界中进行着,这些就构成了海贼王里的多进程。
python中的进程模块multiprocessing多进程举例
# 进程代码
import os
from multiprocessing import Process
def fun_1(str):
print("海贼王子进程id:", os.getpid(), str)
def main():
p1 = Process(target=fun_1, args=('进程1:讨伐凯多',))
p2 = Process(target=fun_1, args=('进程2:革命军在搞事',))
p3 = Process(target=fun_1, args=('进程3:世界政府在观察',))
p1.start()
p2.start()
p3.start()
print('海贼王主进程id:', {os.getpid()})
if __name__ == '__main__':
main()
输出结果(每次运行结果不同):
2 线程
线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程是在进程下执行的,并共享上下文,因此同一个进程下的线程间通信更加容易。子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。
好 ,现在继续我们的海贼之旅。
要灭了凯多可不是一件容易的事,凯多方人多势众,还和大妈结盟,战力杠杠。那怎么搞定他们呢?所有人聚在一起刚正面,那估计路飞还没碰到凯多就累死了,所以为了尽快实现目的,每个角色都找到了自己的对手,王对王、将对将、兵对兵,大家分别PK。路飞、佐罗几人挑凯多和大妈;马尔科vs烬和奎因;甚平vs福斯福;弗兰克vs佐佐木。。。每一场战斗都是讨伐凯多进程当中的线程,分别执行,互不干扰,可以认为是并发。 另外,在海贼王中还有一个角色具有多线程的特性,就是“匪帮”卡彭·贝基,他的坚城果实能力,直接让其可以轻松以一敌百。
python中多线程代码举例
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print ("开始线程:" + self.name)
print_time(self.name, self.counter, 5)
print ("退出线程:" + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
if __name__ == "__main__":
# 创建新线程
thread1 = myThread(1, "线程1-路飞vs凯多", 1)
thread2 = myThread(2, "线程2-甚平vs福斯福", 2)
# 开启新线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("退出讨伐凯多主线程")
输出结果为:
3 协程
协程 (coroutine),又称为微线程,它是实现多任务的另一种方式,只不过是比线程更小的执行单元。因为它自带CPU的上下文,这样只要在合适的时机,我们可以把一个协程切换到另一个协程。
在一个线程中的某个函数中,我们可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。
终于,到我们的真男人-山治出来秀一波了。
西服、香烟的标配,这就是我们的绅士-山治,凭一双脚踢出一个世界的男人。不过在和之国篇中变现还不出彩,在面对黑玛丽亚的时候发生中断无法施展自己的实力,山治只能向罗宾发出求救信号。罗宾这边一收到信号,就yield知道了。整个过程都是山治在执行求救信号,直到信号次数达到一个阀值,罗宾出现了。
python中协程代码理解
def luobin():
r = ''
while True:
n = yield r
if not n:
return
if n == 5:
print("[罗宾] 我听到第 %s 次求救了,我到了" % n)
else:
print('[罗宾] 我听到第 %s 次求救了,马上来' % n)
r = '知道了,马上'
def shanzhi(c):
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[山治] 第 %s 次求救:罗宾快来救我' % n)
r = c.send(n)
if n == 5:
print('[山治] 罗宾你来了!')
else:
print('[山治] 罗宾回答: %s' % r)
c.close()
c = luobin()
shanzhi(c)
输出结果为:
[山治] 第 1 次求救:罗宾快来救我
[罗宾] 我听到第 1 次求救了,马上来
[山治] 罗宾回答: 知道了,马上
[山治] 第 2 次求救:罗宾快来救我
[罗宾] 我听到第 2 次求救了,马上来
[山治] 罗宾回答: 知道了,马上
[山治] 第 3 次求救:罗宾快来救我
[罗宾] 我听到第 3 次求救了,马上来
[山治] 罗宾回答: 知道了,马上
[山治] 第 4 次求救:罗宾快来救我
[罗宾] 我听到第 4 次求救了,马上来
[山治] 罗宾回答: 知道了,马上
[山治] 第 5 次求救:罗宾快来救我
[罗宾] 我听到第 5 次求救了,我到了
[山治] 罗宾你来了!
4 结尾
基础内容看似简单,想要精通也得花不少精力。记录下该文,望日后翻阅,能有醍醐灌顶之功效,哈哈。
更多内容,欢迎关注公众号:Python生活志