1.简述
进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,通俗讲就是自定义一段程序的执行过程,即一个正在运行的程序。线程是进程的基本单位,又称为轻量级进程。
不同的进程在内存中会开辟独立的地址空间,默认进程之间的数据是不共享,线程是由进程创建,所以处在同一个进程中的所有线程都可以访问该进程所包含的地址空间,当然也包含存储在该空间中的所有资源。
线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。协成的原理:利用一个线程,分解一个线程成为多个微线程,注意是从程序级别来分解的
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。
应用场景:
计算密集型操作靠cpu,所以一般使用进程来完成
IO密集型操作由于不占用CPU资源,所以一般使用线程来完成
协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程
为什么使用多线程或多进程?
多线程和多进程可以提供程序的并发处理能力。看下面需求:
现在有10台主机,现在需要监控主机的存过状态,默认使用单线程,如下:
#-*-encoding:utf-8-*-
import time
def f1(arg):
time.sleep(2) #假设ping一次需要2s
print("ping %s主机中..." % arg)
st = time.time() #程序开始时间
host_List = [0,1,2,3,4,5,6,7,8,9,] #假设列表中表示10个主机
for i in host_List:
f1(i)
cost_time = time.time() - st
print('程序耗时:%s' % cost_time)
程序运行结果:
ping 0主机中...
ping 1主机中...
ping 2主机中...
ping 3主机中...
ping 4主机中...
ping 5主机中...
ping 6主机中...
ping 7主机中...
ping 8主机中...
ping 9主机中...
程序耗时:20.002294063568115
发现耗时20s,这仅仅是10台机器,如果100台呢,效率会非常低。假如用了多线程呢?
#-*-encoding:utf-8-*-
import threading
import time
def f1(arg):
time.sleep(2) #假设ping一次需要2s
print("ping %s主机中..." % arg)
st = time.time() #程序开始时间
host_List = [0,1,2,3,4,5,6,7,8,9,] #假设列表中表示10个主机
for i in host_List:
t = threading.Thread(target=f1, args=(i,))
t.start()
t.join()
cost_time = time.time() - st
print('程序耗时:%s' % cost_time)
运行结果:
ping 0主机中...
ping 1主机中...
ping 5主机中...
ping 4主机中...
ping 2主机中...
ping 3主机中...
ping 7主机中...
ping 6主机中...
ping 8主机中...
ping 9主机中...
程序耗时:2.002915382385254
从结果中看出,10个机器启用10个线程并发去独立ping,这样耗时仅仅是单线程的耗时,效率大大提供。所以多进程多线程一般用来提高并发
由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,需要线程锁 - 同一时刻允许一个线程执行操作。
生产者消费者模型
生产者消费者模型是通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度
为什么要使用生产者和消费者模式?
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式?
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。
队列的概念和作用:
队列不是线程,队列是用来组织任务的,将任务加到队列中,任务会按照加入到队列中先后顺序依次执行,如果是同步队列,会在当前线程中执行,如果是异步队列,则操作系统会根据系统资源去创建新的线程去处理队列中的任务,线程的创建、维护和销毁由操作系统管理,系统会给我做很多优化
范例:
实现一个线程不断生成10个数到一个队列中(使用Queue这个模块)
实现一个线程从上面的队列里面不断的取出奇数
实现另外一个线程从上面的队列里面不断取出偶数
#-*-encoding:utf-8-*-
import random,threading,time
from Queue import Queue
#Producer thread
class Producer(threading.Thread):
def __init__(self, t_name, queue):
threading.Thread.__init__(self,name=t_name)
self.data=queue
def run(self):
for i in range(10):
print "%s: %s is producing %d to the queue!" % (time.ctime(), self.getName(), i)
self.data.put(i) #将数据依次存入队列
time.sleep(1)
print "%s: %s finished!" %(time.ctime(), self.getName())
#Consumer thread
class Consumer_even(threading.Thread):
def __init__(self,t_name,queue):
threading.Thread.__init__(self,name=t_name)
self.data=queue
def run(self):
while 1:
try:
val_even = self.data.get(1,5) #get(self, block=True, timeout=None) ,1就是阻塞等待,5是超时5秒
if val_even%2==0:
print "%s: %s is consuming %d in the queue!" % (time.ctime(),self.getName(),val_even)
time.sleep(3)
else:
self.data.put(val_even)
time.sleep(3)
except: #等待输入,超过5秒 就报异常
print "%s: %s finished!" %(time.ctime(),self.getName())
break
class Consumer_odd(threading.Thread):
def __init__(self,t_name,queue):
threading.Thread.__init__(self, name=t_name)
self.data=queue
def run(self):
while 1:
try:
val_odd = self.data.get(1,5)
if val_odd%2!=0:
print "%s: %s is consuming %d in the queue!" % (time.ctime(), self.getName(), val_odd)
time.sleep(3)
else:
self.data.put(val_odd)
time.sleep(3)
except:
print "%s: %s finished!" % (time.ctime(), self.getName())
break
#Main thread
def main():
queue = Queue()
producer = Producer('Producer', queue)
consumer_even = Consumer_even('Consumer_even.', queue)
consumer_odd = Consumer_odd('Consumer_odd.',queue)
producer.start()
consumer_even.start()
consumer_odd.start()
producer.join()
consumer_even.join()
consumer_odd.join()
print 'All threads terminate!'
if __name__ == '__main__':
main()
运行结果:
Sat Jan 20 22:56:46 2018: Producer is producing 0 to the queue!
Sat Jan 20 22:56:46 2018: Consumer_even is consuming 0 in the queue!
Sat Jan 20 22:56:47 2018: Producer is producing 1 to the queue!
Sat Jan 20 22:56:47 2018: Consumer_odd is consuming 1 in the queue!
Sat Jan 20 22:56:48 2018: Producer is producing 2 to the queue!
Sat Jan 20 22:56:49 2018: Producer is producing 3 to the queue!
Sat Jan 20 22:56:49 2018: Consumer_even is consuming 2 in the queue!
Sat Jan 20 22:56:50 2018: Producer is producing 4 to the queue!
Sat Jan 20 22:56:50 2018: Consumer_odd is consuming 3 in the queue!
Sat Jan 20 22:56:51 2018: Producer is producing 5 to the queue!
Sat Jan 20 22:56:52 2018: Consumer_even is consuming 4 in the queue!
Sat Jan 20 22:56:52 2018: Producer is producing 6 to the queue!
Sat Jan 20 22:56:53 2018: Producer is producing 7 to the queue!
Sat Jan 20 22:56:53 2018: Consumer_odd is consuming 5 in the queue!
Sat Jan 20 22:56:54 2018: Producer is producing 8 to the queue!
Sat Jan 20 22:56:55 2018: Consumer_even is consuming 6 in the queue!
Sat Jan 20 22:56:55 2018: Producer is producing 9 to the queue!
Sat Jan 20 22:56:56 2018: Producer finished!
Sat Jan 20 22:56:56 2018: Consumer_odd is consuming 7 in the queue!
Sat Jan 20 22:56:58 2018: Consumer_even is consuming 8 in the queue!
Sat Jan 20 22:56:59 2018: Consumer_odd is consuming 9 in the queue!
Sat Jan 20 22:57:06 2018: Consumer_even finished!
Sat Jan 20 22:57:07 2018: Consumer_odd finished!
All threads terminate!