大聊Python----进程和线程
什么是线程?
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

什么是进程?
程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。
在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。
有了进程为什么还要线程?
进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:
1、进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
2、进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
例如,我们在使用qq聊天, qq做为一个独立进程如果同一时间只能干一件事,那他如何实现在同一时刻 即能监听键盘输入、又能监听其它人给你发的消息、同时还能把别人发的消息显示在屏幕上呢?你会说,操作系统不是有分时么?但我的亲,分时是指在不同进程间的分时呀, 即操作系统处理一会你的qq任务,又切换到word文档任务上了,每个cpu时间片分给你的qq程序时,你的qq还是只能同时干一件事呀。
再直白一点, 一个操作系统就像是一个工厂,工厂里面有很多个生产车间,不同的车间生产不同的产品,每个车间就相当于一个进程,且你的工厂又穷,供电不足,同一时间只能给一个车间供电,为了能让所有车间都能同时生产,你的工厂的电工只能给不同的车间分时供电,但是轮到你的qq车间时,发现只有一个干活的工人,结果生产效率极低,为了解决这个问题,应该怎么办呢?。。。。没错,你肯定想到了,就是多加几个工人,让几个人工人并行工作,这每个工人,就是线程!
Python threading模块
最简单的多线程程序:
import threading # 多线程模块
import time def run(n):
print("test name is",n)
time.sleep(2) test1 = threading.Thread(target=run,args=("test1",))
test2 = threading.Thread(target=run,args=("test2",)) test1.start() # 开始多线程 test1
test2.start() # 开始多线程 test2
显示结果:

用类的方式实现最简单的多线程程序:
import threading
import time class MyThread(threading.Thread):
def __init__(self,n):
super(MyThread,self).__init__()
self.n = n def run(self):
print("thr tast name is",self.n)
time.sleep(2) test1 = MyThread("test1")
test2 = MyThread("test2") test1.start()
test2.start()
程序的执行结果为:

Join(等待线程)
使用Join时,程序会等子线程结束完毕,不会等主线程结束完毕。
像下面的程序,想知道多线程运算的时间,所以需要join模块,否则无法计算多线程运算的时间!
import threading
import time def run(n): # 子线程
print("test name is",n)
time.sleep(2) start_time = time.time()
str_objects = []
for i in range(50): # 主线程
t= threading.Thread(target=run,args=("test-%s"%i,))
t.start()
str_objects.append(t) for i in str_objects:
i.join() # 等待 相当wait() print("The coast times is ",time.time() - start_time) # 主线程
程序执行的结果为:
test name is test-0
test name is test-1
test name is test-2
test name is test-3
test name is test-4
·······················
·······················
test name is test-46
test name is test-47
test name is test-48
test name is test-49
The coast times is 2.009114980697632
Daemon (守护线程)
使用Daemon时,程序会等主线程结束完毕,不会等守护线程(子线程)结束完毕。
可以看下面的程序:
import threading
import time def run(n): # 子线程
print("test name is",n)
time.sleep(2) start_time = time.time()
str_objects = []
for i in range(50): # 主线程
t= threading.Thread(target=run,args=("test-%s"%i,))
t.setDaemon(True) # 把当前线程设置为守护线程
t.start()
str_objects.append(t) # for i in str_objects:
# i.join() # 等待 相当wait() print("The coast times is ",time.time() - start_time) # 主线程
程序的执行结果为:
test name is test-0
test name is test-1
test name is test-2
test name is test-3
test name is test-4
·······················
·······················
test name is test-46
test name is test-47
test name is test-48
test name is test-49
The coast times is 0.020000934600830078
那么实际的应用场景是:
当你写一个SocketServer,而SocketServer的实际情况是每一个链接过来,SocketServer就会为这个链接分配一个新的线程,启动一个新线程之后,那么如果手动的把SocketServer停掉,那么这种情况下手能停服务,那它就要宕了,那么这种情况还需等线程结束吗?那就是不等线程结束了,它就结束了,像上面的这种场景就可以把每一个Socket线程都可以设置为守护线程,主线程一宕也就是主线程一退出全部都退出。
上面的线程相互之间无沟通,无数据共享,而线程是可以相互沟通和共享数据的,所以下面的程序是线程之间相互沟通和共享数据的!若想实现这个功能,则需要使用线程锁!
那么什么是线程锁(互斥锁)呢?
一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况?
import threading
import time def run(n): # 子线程
lock.acquire() # 开锁
global num
num += 1
time.sleep(0.1)
lock.release() # 关锁 lock = threading.Lock()
num = 0 # 主线程
str_objects = []
for i in range(50): # 主线程
t= threading.Thread(target=run,args=("test-%s"%i,))
# t.setDaemon(True) # 把当前线程设置为守护线程
t.start()
str_objects.append(t) for i in str_objects:
i.join() # 等待 相当wait() print("--------------all threads has finished ...",threading.current_thread(),threading.active_count())
print("num:",num)
程序运行后的结果为:

正常来讲,这个num结果应该是50, 但在python 2.7上多运行几次,会发现,最后打印出来的num结果不总是50,为什么每次运行的结果不一样呢? 哈,很简单,假设你有A,B两个线程,此时都 要对num 进行减1操作, 由于2个线程是并发同时运行的,所以2个线程很有可能同时拿走了num=0这个初始变量交给cpu去运算,当A线程去处完的结果是1,但此时B线程运算完的结果也是1,两个线程同时CPU运算的结果再赋值给num变量后,结果就都是1。那怎么办呢? 很简单,每个线程在要修改公共数据时,为了避免自己在还没改完的时候别人也来修改此数据,可以给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。
为什么会运行5s呢?
因为它要执行50次,每次0.1s,想当于是个串行,所以会出现这样的效果,其实像上面的程序里的线程锁适用于Python2.6/2.7,因为在Python3里已经对线程锁进行优化了,在代码里不需要在对线程锁进行编写。
GIL VS Lock
机智的同学可能会问到这个问题,就是既然你之前说过了,Python已经有一个GIL来保证同一时间只能有一个线程来执行了,为什么这里还需要lock? 注意啦,这里的lock是用户级的lock,跟那个GIL没关系 ,具体我们通过下图来看一下+配合我现场讲给大家,就明白了。

那你又问了, 既然用户程序已经自己有锁了,那为什么C python还需要GIL呢?加入GIL主要的原因是为了降低程序的开发的复杂度,比如现在的你写python不需要关心内存回收的问题,因为Python解释器帮你自动定期进行内存回收,你可以理解为python解释器里有一个独立的线程,每过一段时间它起wake up做一次全局轮询看看哪些内存数据是可以被清空的,此时你自己的程序 里的线程和 py解释器自己的线程是并发运行的,假设你的线程删除了一个变量,py解释器的垃圾回收线程在清空这个变量的过程中的clearing时刻,可能一个其它线程正好又重新给这个还没来及得清空的内存空间赋值了,结果就有可能新赋值的数据被删除了,为了解决类似的问题,python解释器简单粗暴的加了锁,即当一个线程运行时,其它人都不能动,这样就解决了上述的问题,这可以说是Python早期版本的遗留问题。
RLock(递归锁)
说白了就是在一个大锁中还要再包含子锁
import threading,time def run1():
print("grab the first part data")
lock.acquire()
global num
num +=1
lock.release()
return num
def run2():
print("grab the second part data")
lock.acquire()
global num2
num2+=1
lock.release()
return num2
def run3():
lock.acquire()
res = run1()
print('--------between run1 and run2-----')
res2 = run2()
lock.release()
print(res,res2)
if __name__ == '__main__':
num,num2 = 0,0
lock = threading.RLock()
for i in range(10):
t = threading.Thread(target=run3)
t.start()
while threading.active_count() != 1:
print(threading.active_count())
else:
print('----all threads done---')
print(num,num2)
程序执行后的结果为:

······························································
·····························································

Semaphore(信号量)
互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
import threading,time def run(n):
semaphore.acquire()
time.sleep(1)
print("run the thread: %s\n" %n)
semaphore.release() if __name__ == '__main__':
num= 0
semaphore = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
for i in range(20):
t = threading.Thread(target=run,args=(i,))
t.start()
while threading.active_count() != 1:
pass #print threading.active_count()
else:
print('----all threads done---')
print(num)
程序运行后的结果:
run the thread: 2
run the thread: 4
run the thread: 3
run the thread: 1
run the thread: 0
run the thread: 7
run the thread: 8
run the thread: 6
run the thread: 5
run the thread: 9
run the thread: 12
run the thread: 11
run the thread: 10
run the thread: 14
run the thread: 13
run the thread: 15
run the thread: 19
run the thread: 17
run the thread: 16
run the thread: 18
----all threads done---
Events
事件是一个简单的同步对象;
该事件代表一个内部标志和线程
可以等待设置标志,或者自己设置或清除标志。
event = threading.Event()
event.wait()#客户端线程可以等待设置标志
event.set()#一个服务器线程可以设置或重置它
event.clear()
如果设置了标志,则wait方法不会执行任何操作。
如果该标志被清除,则等待将被阻塞,直到它再次被设置为止。
任意数量的线程都可以等待同一事件。
通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
import time
import threading event = threading.Event() def lighter():
count = 0
event.set()
while True:
if count > 5 and count <= 10:
event.clear()
print("\033[41;1m 红灯亮 \033[0m")
elif count > 10:
event.set()
count = 0
else:
print("\033[42;1m 绿灯亮 \033[0m")
time.sleep(1)
count += 1 def car(name):
while True:
if event.is_set():
print("[%s] running..."% name)
time.sleep(1)
else:
print("[%s] sees red light,waiting..." %name)
event.wait()
print("\033[34;1m[%s] green light is on,start going...\033[0m"%name) light = threading.Thread(target=lighter,)
light.start() cars = threading.Thread(target=car,args=("Tesla",))
cars.start()
程序执行后的结果为:
绿灯亮
[Tesla] running....
绿灯亮
[Tesla] running....
绿灯亮
[Tesla] running....
绿灯亮
[Tesla] running....
绿灯亮
[Tesla] running....
绿灯亮
[Tesla] running....
红灯亮
[Tesla] sees red light,waiting...
红灯亮
[Tesla] sees red light,waiting...
红灯亮
[Tesla] sees red light,waiting...
红灯亮
[Tesla] sees red light,waiting...
红灯亮
[Tesla] sees red light,waiting...
[Tesla] green light is on,start going...
绿灯亮
[Tesla] running....
大聊Python----进程和线程的更多相关文章
- python进阶:Python进程、线程、队列、生产者/消费者模式、协程
一.进程和线程的基本理解 1.进程 程序是由指令和数据组成的,编译为二进制格式后在硬盘存储,程序启动的过程是将二进制数据加载进内存,这个启动了的程序就称作进程(可简单理解为进行中的程序).例如打开一个 ...
- python 进程和线程
python中的进程.线程(threading.multiprocessing.Queue.subprocess) Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就 ...
- Python进程、线程、协程
进程和线程的解释 进程(process)和线程(thread)是操作系统的基本概念,计算机的核心是CPU,它承担了所有的计算任务: 单个CPU一次只能运行一个任务,代表单个CPU总是运行一个进程,其他 ...
- python进程、线程、协程(转载)
python 线程与进程简介 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资 ...
- Python进程和线程
引入进程和线程的概念及区别 1.线程的基本概念 概念 线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但 ...
- Python进程、线程、协程详解
进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配.任务的调度. ...
- python 进程、线程与协程的区别
进程.线程与协程区别总结 - 1.进程是计算器最小资源分配单位 - 2.线程是CPU调度的最小单位 - 3.进程切换需要的资源很最大,效率很低 - 4.线程切换需要的资源一般,效率一般(当然了在不考虑 ...
- [ Python - 14 ] python进程及线程编程
什么是进程: 简单来讲,进程就是操作系统中运行的程序或任务,进程和程序的区别在于进程是动态的,而程序是静态的.进程是操作系统资源管理的最小单位. 什么是线程: 线程是进程的一个实体,是cpu调度和分派 ...
- Python进程、线程、协程之间的关系
一.从操作系统角度 操作系统处理任务, 调度单位是 进程 和 线程 . 1.进程: 表示一个程序的执行活动 (打开程序.读写程序数据.关闭程序) 2.线程: 执行某个程序时, 该进程调度的最小执行单位 ...
- Python进程、线程、协程的对比
1. 执行过程 每个线程有一个程序运行的入口.顺序执行序列和程序的出口.但是线程不能够独立执行,必须依存在进程中,由进程提供多个线程执行控制.每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该 ...
随机推荐
- iOS开发热更新JSPatch
JSPatch,只需在项目中引入极小的引擎,就可以使用JavaScript调用任何Objective-C的原生接口,获得脚本语言的能力:动态更新APP,替换项目原生代码修复bug. 是否有过这样的经历 ...
- 【beta】Scrum站立会议第1次....11.3
beta阶段,我们nice!团队将进行为期两周的冲刺,Scrum站立会议10次. 小组名称:nice! 组长:李权 成员:于淼 刘芳芳韩媛媛 宫丽君 项目内容:约跑app(约吧) 时间:2016.1 ...
- 身份证验证php
/** * 验证身份证号 * @param $vStr * @return bool */ function isCreditNo($vStr) { $vCity = array( ...
- Swoole和Swoft的那些事 (Http/Rpc服务篇)
https://www.jianshu.com/p/4c0f625d5e11 Swoft在PHPer圈中是一个门槛较高的Web框架,不仅仅由于框架本身带来了很多新概念和前沿的设计,还在于Swoft是一 ...
- Dojo初探
Dojo 是一个由 Dojo 基金会开发的 Javascript 工具包, 据说受到 IBM 的永久支持,其包括四个部分: dojo, dijit, dojox, util dojo: 有时也被称作 ...
- MATLAB中mat2gray的用法【转】
函数简介 函数功能:实现图像矩阵的归一化操作.所谓"归一化"就是使矩阵的每个元素的值都在0和1之间.该函数在数字图像处理中经常用到. 调用格式: I = mat2gray(A, [ ...
- K-means聚类算法与EM算法
K-means聚类算法 K-means聚类算法也是聚类算法中最简单的一种了,但是里面包含的思想却不一般. 聚类属于无监督学习.在聚类问题中,给我们的训练样本是,每个,没有了y. K-means算法是将 ...
- 【bzoj1742】[Usaco2005 nov]Grazing on the Run 边跑边吃草 区间dp
题目描述 John养了一只叫Joseph的奶牛.一次她去放牛,来到一个非常长的一片地,上面有N块地方长了茂盛的草.我们可以认为草地是一个数轴上的一些点.Joseph看到这些草非常兴奋,它想把它们全部吃 ...
- BZOJ4247 挂饰(动态规划)
相当于一个有负体积的背包.显然如果确定了选哪些,应该先把体积小的挂上去.于是按体积从小到大排序,就是一个裸的背包了. #include<iostream> #include<cstd ...
- 进程池-限制同一时间在CPU上运行的进程数
if __name__=='__main__' : 为了区分你是主动执行这个脚本,还是从别的地方把它当做一个模块去调用. 如果是主动执行,则执行.如果是调用的,则不执行主体. 1. 串行:切记切记: ...