大聊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寄存器,称为线程的上下文,该 ...
随机推荐
- NSDate常用的日期操作
// 当前时间创建NSDate NSDate *myDate = [NSDate date]; NSLog(@"myDate = %@",myDate); //从现在开始的24小时 ...
- Debian 7 amd64问题
Debian 7 发布了有1段时间,最近才在自己的电脑硬盘安装,用户体验还算可以.在安装Debian的过程中,有问题还是要记录一下的. 注意:遇到的问题跟硬件体系相关,可能在个别电脑没法重现. 1.默 ...
- C# .net 获取外网ip
public string GetIP() { string strUrl = "http://www.ip138.com/ip2city.asp"; //获得IP的网址了 Uri ...
- Vim新手节省时间的10多个小技巧
Vim新手节省时间的10多个小技巧 Vim 是很多开发者的首选编辑器,通过设置正确的命令和快捷方式,它可以帮你更快的完成工作.这篇文章我们为 Vim 新手提供一些快捷键等方面的小技巧,帮你提升工作效率 ...
- LoadRunner数据库监控指标
SQL Server 注:以下指标取自SQL Server自身提供的性能计数器. 指标名称 指标描述 指标范围 指标单位 1.SQL Server中访问方法(Access Methods)对象包含的性 ...
- Spring事务管理Transaction
Spring提供了许多内置事务管理器实现: DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,数据源事务管理器, ...
- An Introduction to Lock-Free Programming
Lock-free programming is a challenge, not just because of the complexity of the task itself, but bec ...
- Python 嵌套函数和闭包
Python 嵌套函数和闭包 1.函数嵌套 如果在一个函数内部定义了另一个函数,我们称外部的函数为外函数,内部的函数为内函数,如下代码: def out_func(): def inner_func1 ...
- Oracle DB_LINK如何使用
语句,或可通过可视化操作 -- Create database link create database link DBL_TESTconnect to UID identified by PSWus ...
- BZOJ1036:[ZJOI2008]树的统计——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=1036 题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来 ...