您的位置:首页 > 编程语言 > Python开发

python线程与进程

2015-12-25 14:13 731 查看
建议用pycharm阅读,可以收缩,也可以测试

'''
IO多路复用

I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程
序进行相应的读写操作。目前支持I/O多路复用的系统调用有 select,poll,epoll

应用场景:
服务器需要同时处理多个处于监听状态或者多个连接状态的套接字。
服务器需要同时处理多种网络协议的套接字。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import select
import socket
import Queue

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(False)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR , 1)
server_address= ('192.168.1.5',8080)
server.bind(server_address)
server.listen(10)

#select轮询等待读socket集合
inputs = [server]
#select轮询等待写socket集合
outputs = []
message_queues = {}
#select超时时间
timeout = 20

while True:
print "等待活动连接......"
readable , writable , exceptional = select.select(inputs, outputs, inputs, timeout)

if not (readable or writable or exceptional) :
print "select超时无活动连接,重新select...... "
continue;
#循环可读事件
for s in readable :
#如果是server监听的socket
if s is server:
#同意连接
connection, client_address = s.accept()
print "新连接: ", client_address
connection.setblocking(0)
#将连接加入到select可读事件队列
inputs.append(connection)
#新建连接为key的字典,写回读取到的消息
message_queues[connection] = Queue.Queue()
else:
#不是本机监听就是客户端发来的消息
data = s.recv(1024)
if data :
print "收到数据:" , data , "客户端:",s.getpeername()
message_queues[s].put(data)
if s not in outputs:
#将读取到的socket加入到可写事件队列
outputs.append(s)
else:
#空白消息,关闭连接
print "关闭连接:", client_address
if s in outputs :
outputs.remove(s)
inputs.remove(s)
s.close()
del message_queues[s]
for s in writable:
try:
msg = message_queues[s].get_nowait()
except Queue.Empty:
print "连接:" , s.getpeername() , '消息队列为空'
outputs.remove(s)
else:
print "发送数据:" , msg , "到", s.getpeername()
s.send(msg)

for s in exceptional:
print "异常连接:", s.getpeername()
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
del message_queues[s]
'''

'''
进程和线程的区别和关系:

对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事
本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。
有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多
件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像Word这种复杂的进程可以有多个线程,多个线
程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替
运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核CPU才可能实现。
线程是最小的执行单元,而进程由至少一个线程组成。如何调度进程和线程,完全由操作系统决定,程序自己不能决定什
么时候执行,执行多长时间。
'''

'''
python的进程

multiprocessing包的组件Process, Queue, Pipe, Lock等组件提供了与多线程类似的功能。使用这些组件,可以方便
地编写多进程并发程序。
'''

'''
Queue队列

Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。put方法用以插入数据到队列中,put方法还有
两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时
间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出
Queue.Full异常。

get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True
(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,
有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常。

from multiprocessing import Process, Queue

def offer(queue):
queue.put("Hello World")

if __name__ == '__main__':
q = Queue()
p = Process(target=offer, args=(q,))
p.start()
print q.get()
'''

'''
Pipes管道

Pipe方法返回(conn1, conn2)代表一个管道的两个端。Pipe方法有duplex参数,如果duplex参数为True(默认值)那么
这个管道是全双工模式,也就是说conn1和conn2均可收发。duplex为False,conn1只负责接受消息,conn2只负责发送消息
send和recv方法分别是发送和接受消息的方法。例如,在全双工模式下,可以调用conn1.send发送消息,conn1.recv接
收消息。如果没有消息可接收,recv方法会一直阻塞。如果管道已经被关闭,那么recv方法会抛出EOFError。

from multiprocessing import Process, Pipe

def send(conn):
conn.send("Hello World")
conn.close()

if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target=send, args=(child_conn,))
p.start()
print parent_conn.recv()
'''

'''
创建进程示例

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process
import os

def run_proc(name):
print 'Run child process %s (%s)...' % (name, os.getpid())

if __name__=='__main__':
print 'Parent process %s.' % os.getpid()
p = Process(target=run_proc, args=('test',))
print 'Process will start.'
p.start()
print 'Process end.'
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动。
注意:由于进程之间的数据需要各自持有一份,所以创建进程需要的非常大的开销。
'''

'''
进程锁示例

from multiprocessing import Process, Array, RLock
def Foo(lock,temp,i):
"""
将第0个数加100
"""
lock.acquire()
temp[0] = 100+i
for item in temp:
print i,'----->',item
lock.release()

lock = RLock()
temp = Array('i', [11, 22, 33, 44])

for i in range(20):
p = Process(target=Foo,args=(lock,temp,i,))
p.start()
'''

'''
进程池示例

在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时
间。当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百
个,上千个目标,手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。
Pool可以提供指定数量的进程供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程
用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程
来它。

#!/usr/bin/env python
#coding:utf-8
from multiprocessing import Pool
import os, time, random

def long_time_task(name):
print 'Run task %s (%s)...' % (name, os.getpid())
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print 'Task %s runs %0.2f seconds.' % (name, (end - start))

if __name__=='__main__':
print 'Parent process %s.' % os.getpid()
p = Pool(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print 'Waiting for all subprocesses done...'
p.close()
p.join()
print 'All subprocesses done.'
join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4,
因此,最多同时执行4个进程。
'''

'''
进程间共享数据

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process, Queue
import os, time, random

# 写数据进程执行的代码:
def write(q):
for value in ['A', 'B', 'C']:
print 'Put %s to queue...' % value
q.put(value)
time.sleep(random.random())

# 读数据进程执行的代码:
def read(q):
while True:
value = q.get(True)
print 'Get %s from queue.' % value

if __name__=='__main__':
# 父进程创建Queue,并传给各个子进程:
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# 启动子进程pw,写入:
pw.start()
# 启动子进程pr,读取:
pr.start()
# 等待pw结束:
pw.join()
# pr进程里是死循环,无法等待其结束,只能强行终止:
pr.terminate()
进程间默认无法共享数据

'''

'''
Python的线程

多任务可以由多进程完成,也可以由一个进程内的多线程完成。进程是由若干线程组成的,一个进程至少有一个线程。
Python的标准库提供了两个模块:thread和threading,thread是低级模块,threading是高级模块,对thread进行了
封装。绝大多数情况下,我们只需要使用threading这个高级模块。启动一个线程就是把一个函数传入并创建Thread实例,然
后调用start()开始执行
'''

'''
python的多线程模块:threading

Thread                  #线程执行的对象

start               线程准备就绪,等待CPU调度
setName             为线程设置名称
getName             获取线程名称
setDaemon           设置为后台线程或前台线程(默认)
如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不
论成功与否,均停止如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行
完毕后,等待前台线程也执行完成后,程序停止
join                逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
run                 线程被cpu调度后执行Thread类对象的run方法
Rlock                   #线程锁:可重入锁对象.使单线程可以在此获得已获得了的锁(递归锁定)

acquire             为线程加锁
release             为线程解锁
Event                   #python线程的事件用于主线程控制其他线程的执行。

set                 将全局变量设置为True
wait                事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行
event.wait方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞
clear               将全局变量设置为False
Semaphore               为等待锁的线程提供一个类似等候室的结构
BoundedSemaphore        与Semaphore类似,只是不允许超过初始值
Time                    与Thread相似,只是他要等待一段时间后才开始运行
activeCount()           当前活动的线程对象的数量
currentThread()         返回当前线程对象
enumerate()             返回当前活动线程的列表
settrace(func)          为所有线程设置一个跟踪函数
setprofile(func)        为所有线程设置一个profile函数

'''

'''
线程示例

#!/usr/bin/env python
#coding:utf-8
import threading
import time

def show(arg):
time.sleep(1)
print 'thread'+str(arg)

for i in range(10):
t = threading.Thread(target=show, args=(i,))
t.start()
print 'main thread stop'
'''

'''
线程锁示例

多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,
所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个
线程同时改一个变量,把内容给改乱了。

#!/usr/bin/env python
#coding:utf-8
import threading
import time
gl_num = 0
def show(arg):
global gl_num
time.sleep(1)
gl_num +=1
print gl_num

for i in range(10):
t = threading.Thread(target=show, args=(i,))
t.start()
print 'main thread stop'

由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,CPU接着执行其他线程
如果按上例的话会出现一种情况多个线程同时修改一份内存资源,造成数据的修改混乱那么线程锁可以解决这个问题
#!/usr/bin/env python
#coding:utf-8import threading
import time
gl_num = 0
lock=threading.RLock()
def show(arg):
lock.acquire()
global gl_num
time.sleep(1)
gl_num +=1
print gl_num
lock.release()
for i in range(10):
t = threading.Thread(target=show, args=(i,))
t.start()
print 'main thread stop'

因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python
线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全
局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也
只能用到1个核。
GIL是Python解释器设计的历史遗留问题,通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个
不带GIL的解释器。所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那
只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。
不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有
各自独立的GIL锁,互不影响。
多线程编程,模型复杂,容易发生冲突,必须用锁加以隔离,同时,又要小心死锁的发生。
Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦。
'''

'''
线程的事件示例

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading

def do(event):
print 'start'
event.wait()
print 'execute'

event_obj = threading.Event()
for i in range(10):
t = threading.Thread(target=do, args=(event_obj,))
t.start()

event_obj.clear()
inp = raw_input('input:')
if inp == 'true':
event_obj.set()
'''

'''
协程简介

线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继
续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。
协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;
'''

'''
协程示例

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from greenlet import greenlet

def test1():
print 12
gr2.switch()
print 34
gr2.switch()

def test2():
print 56
gr1.switch()
print 78

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
'''

'''
进程vs线程

我们可以把任务分为计算密集型和IO密集型。
计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算
能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越
低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。
计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。用Python的话适合多进程
第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部
分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也
有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。这时候不需要cpu做过多的计算,应当用多线程。

'''
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息