一些简单的进程和线程概念
单进程和单线程
程序在运行时可能有多个进程,每个进程中可能有多个线程,单cpu一次只能处理一个进程,采用分片处理让人们感觉是同时在运行。
多进程和多线程
一个物理cup可以分几核,也就可以理解为可以同时处理多个线程, 而且一个线程中的进程可以被不同的cpu处理(这种情况在python中不可能出现这种情况,因为python中有JIL,全局解释器锁,防止多个线程同时被执行,一次只能执行一个线程,后来python创始人使用多进程处理这个问题)
- 多进程之间资源共享的问题
一个线程的多个进程共享进程的资源,一个程序的多个线程被不同cpu执行时需要3份相同的程序资源,会造成资源的浪费。
多进程和多线程之间的选择
如果是计算密集型,计算大量的数据,计算数据是使用cpu,所以要使用多进程;如果是IO密集型,IO操作不用一直占用cpu,所以使用多线程较好。IO和计算如果都多则多进程和多线程较好。
线程安全的问题
线程共享内存(共享主线程的内存空间,因为如果不贡献,内存空间的占用会成指数增长,内存消耗太大),进程不共享内存,因为每个进程之间是的内存空间完全独立(单进程)。所以当10个线程要修改同一份数据的时候,有可能打断另一个进程的数据的操作。这里就涉及到线程锁。
#!/usr/bin/env python
#coding:utf-8
import threading
import time
import random
num = 0
def Sum():
global num
time.sleep(2)
num += 1
print num
for i in range(100):
feitian = threading.Thread(target=Sum)
feitian.start()
#输出结果:应该是1-100,但是他并没有加到100,是因为,当两个进程同时渠道num这个资源时,同时对num进行加一。所以我们需要加上线程锁,当有线程对资源进行操作时,资源就不能被其他资源获取。
改进程序:
#!/usr/bin/env python
#coding:utf-8
import threading
import time
import random
num = 0
def Sum():
global num
time.sleep(2)
lock.acquire()
num += 1
print num
lock.release()
lock = threading.Lock()
for i in range(100):
feitian = threading.Thread(target=Sum)
feitian.start()
#注意:这里又可以引出cpu最底层也是单线程的,同一时刻只能有一个线程在执行,在进行分片,只是你感觉不到,分片默认100条cpu指令。也就是说上面的程序在我释放锁之后,不是立即抢占cpu资源,而是等待他执行100条cpu指令之后才开始。但是如果有sleep的时候,因为sleep不占cpu资源,所以就可能直接切。
#这里还要注意当你连续使用两次lock.acquire()的时候,你会发现你的程序阻塞了,因为当他获得一把锁之后(独占cpu),当另一条语句获取锁的时候,cpu会认为是两一个线程获取锁,等待锁的释放,相当于自己等待自己释放,自己又不会释放,所以就阻塞了。如下核心程序:
lock.acquire()
num += 1
lock.acquire()
num1 += 2
print num
lock.release()
lock.release()
如果你有这种需求怎么办?
这里就有lock = threading.RLock()还有另外一种比较重要,在下面。
重要
mysql的最大连接数也是相同的原理
lock = threading.BoundedSemaphore(4)
他就是最多同时有几个人获取资源,但是可能造成线程安全
#!/usr/bin/env python
#coding:utf-8
import threading
import time
import random
num = 0
num1 =0
def Sum():
global num
time.sleep(2)
lock.acquire()
num += 1
print num
lock.release()
lock = threading.BoundedSemaphore(4)
for i in range(100):
feitian = threading.Thread(target=Sum)
feitian.start()
#你会返现屏幕输出已经乱码,因为,多个线程同时在抢tty。不懂的伙伴可以对比一下前面的进程锁的程序。
python中事件触发
#!/usr/bin/env python
#coding:utf-8
import threading
import time
from logging import thread
def Producer():
print u"等人来买包子"
event.wait()
#在这里阻塞,直到遇到event.set()
event.clear()
print u'sb is coming for baozi'
print "chef:make a baozi"
time.sleep(5)
print '你的包子好了'
event.set()
def Consumer():
print u"去买包子"
event.set()
time.sleep(2)
print "waiting for ba to be eat"
event.wait()
print u'thank'
event = threading.Event()
p = threading.Thread(target=Producer)
c = threading.Thread(target=Consumer)
p.start()
c.start()
#输出结果:
等人来买包子
去买包子
sb is coming for baozi
chef:make a baozi
waiting for ba to be eat
还尼玛么好
还尼玛么好
还尼玛么好
你的包子好了
thank
稍微改进一下就相当于select模型
#!/usr/bin/env python
#coding:utf-8
import threading
import time
def Producer():
print u"等人来买包子"
event.wait()
event.clear()
print u'sb is coming for baozi'
print "chef:make a baozi"
time.sleep(5)
print '你的包子好了'
event.set()
def Consumer():
print u"去买包子"
event.set()
time.sleep(2)
print "waiting for ba to be eat"
while True:
if event.is_set(): # 相当于异步select,一直在这里检测;
print u'thank'
break
else:
print "还尼玛么好"
time.sleep(1)
#这个sleep相当于在做自己的事情
event = threading.Event()
p = threading.Thread(target=Producer)
c = threading.Thread(target=Consumer)
p.start()
c.start()
python中提供的多线程模块
threading模块
- start()
立即执行程序的线程 - getName()
获得线程的名字 - setName()
设置线程的名字 - IsDaemon()
是否是守护线程看,也就是是否等待子线成执行完毕在退出(默认为false,等子进程执行完毕在退出) - setDaemon()
设置子进程是否为守护线程,(在线程开始执行之前设置否则报错,子线在主线程结束后自动销毁) - join()
主线程执行到join()之后等待子线成执行,主线程停止执行,等待子线成执行完毕看,然后主线程在继续执行(有一个参数就是等待时间) run()
执行指定的函数线程并不是越多越好,到达一定程度,他会成为程序的累赘,因为时间片的关系,进程之间的切换要保存上下文,切换上下文。
练习
- 实例1
#!/usr/bin/env python
#coding:utf-8
from threading import Thread
def Foo(arg):
print arg
print 'before'
#上面是程序在执行的时候创建一个主线程,下面是程序在执行的时候进行的子线程
t1 = Thread(target=Foo,args=('dd',))
#指定子线成执行的函数和参数
t1.setDaemon(True)
t1.start()
t1.join()
#让他执行他不一定马上执行,不一定能立刻得到cpu
print t1.isDaemon()
print 'after'
#输出结果:
before
ddTrue
after
- 实例2
#!/usr/bin/env python
#coding:utf-8
import time
from threading import Thread
class MyThread(Thread):
def run(self):
time.sleep(10)
print '我是线程'
def Bar():
print 'bar'
t1 = MyThread(target='bar',args=())
#执行继承Thread过来的构造函数
t1.start()
print 'over'
#输出结果
over
我是线程
- 实例3
#!/usr/bin/env python
#coding:utf-8
import time
from threading import Thread
class MyThread(Thread):
def run(self):
print 'MyThred'
Thread.run(self)
#调用了父类的run方法
def Bar():
print 'bar'
t1 = MyThread(target=Bar)
t1.start()
输出结果:
MyThred
bar
- 实例四(生产者消费者模型)
这个非常基础,像hello world一样。
#作用
- 解耦 让各个模块之间的关联性降到最低
- 支持并发
- 支持忙闲不均
#!/usr/bin/env python
#coding:utf-8
from threading import Thread
from Queue import Queue
import time
#队列:先进先出和堆栈不同的是:堆栈是后进后出
class Producer(Thread):
def __init__(self,name,queue):
'''
@param name:生产者的名称
@param queue:容器
'''
self.__Name = name
self.__Queue = queue
super(Producer,self).__init__()
def run(self):
while True:
if self.__Queue.full():
time.sleep(1)
else:
self.__Queue.put('包子')
time.sleep(1)
print '%s 生产了一个包子' %(self.__Name,)
#Thread.run(self)
class Consumer(Thread):
def __init__(self,name,queue):
'''
@param name:生产者的名称
@param queue:容器
'''
self.__Name = name
self.__Queue = queue
super(Consumer,self).__init__()
def run(self):
while True:
if self.__Queue.empty():
time.sleep(1)
else:
self.__Queue.get()
time.sleep(1)
print '%s 消费了一个包子' %(self.__Name,)
#Thread.run(self)
#有关线程安全(同时对同一个文件进行操作,该怎样选择),下面创建一个存放商品的仓库,他就定义了优先级,线程是安全的
que = Queue(maxsize=100)
feitian1 = Producer('飞天1',que)
feitian1.start()
feitian2 = Producer('飞天2',que)
feitian2.start()
feitian3 = Producer('飞天3',que)
feitian3.start()
for item in range(20):
name = 'love%d' %(item,);
temp = Consumer(name,que)
temp.start()
'''
que.put('1')
que.put('2')
print que.qsize()
print que.get()
print que.get()
'''
下面有一个更好的程序来理解生产者和消费者的模型,希望对大家有帮助
#!/usr/bin/env python
#coding:utf-8
import threading
import Queue
import time
import random
def Produce():
while True:
q.put("包子")
print "生产了一个包子"
time.sleep(random.randrange(5))
def Cousume():
while True:
try:
q.get_nowait()
print "吃了一个包子"
except Exception:
print "等着吃包子"
time.sleep(random.randrange(3))
q = Queue.Queue()
produce1 = threading.Thread(target=Produce)
produce2 = threading.Thread(target= Produce)
produce1.start()
produce2.start()
laogou = threading.Thread(target=Cousume)
hanlong = threading.Thread(target=Cousume)
laogou.start()
hanlong.start()