python 多线程, 多进程, 协程

不打扰是莪最后的温柔 提交于 2021-02-14 14:31:10

1. 介绍:

threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。python当前版本的多线程库没有实现优先级、线程组,线程也不能被停止、暂停、恢复、中断。

2. 1  线程执行函数

#!/bin/python
#coding:utf8

import threading
import time

def action(arg):
    time.sleep(1)
    print "this ia arg %s \n" % arg
thread_list = []
for i in xrange(4):
    t = threading.Thread(target=action,args=(i,))
    t.start()
    thread_list.append(t)

for t in thread_list:
    t.join()

print 'main end'
# coding:utf-8

import threading
import time

gl_num = 0

lock = threading.RLock()


# 调用acquire([timeout])时,线程将一直阻塞,
# 直到获得锁定或者直到timeout秒后(timeout参数可选)。
# 返回是否获得锁。
def Func():
    lock.acquire()
    global gl_num
    gl_num += 1
    time.sleep(1)
    print gl_num
    lock.release()


for i in range(10):
    t = threading.Thread(target=Func)
    t.start()


在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)

2.2 线程继承类

import threading
import time

class MyThread(threading.Thread):
    def __init__(self,arg):
        super(MyThread,self).__init__()
        self.arg = arg

    def run(self):
        time.sleep(1)
        print 'this arg is %s \n' % self.arg
thread_list = []
for i in xrange(4):
    t = MyThread(i)
    t.setDaemon(True)
    t.start()
    thread_list.append(t)

for i in thread_list:
    t.join()


print 'main end'

 

3.介绍:

构造方法: 
Thread(group=None, target=None, name=None, args=(), kwargs={}) 

  group: 线程组,目前还没有实现,库引用中提示必须是None; 
  target: 要执行的方法; 
  name: 线程名; 
  args/kwargs: 要传入方法的参数。

实例方法: 
  isAlive(): 返回线程是否在运行。正在运行指启动后、终止前。 
  get/setName(name): 获取/设置线程名。 

  start():  线程准备就绪,等待CPU调度
  is/setDaemon(bool): 获取/设置是后台线程(默认前台线程(False))。(在start之前设置)

    如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,主线程和后台线程均停止
         如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
  start(): 启动线程。 
  join([timeout]): 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)。

  join()阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout,即使设置了setDeamon(True)主线程依然要等待子线程结束。

 

 

二、多进程

 multiprocessing
python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

1.1 函数

import multiprocessing
import time

def worker(interval):
    n = 5

    while n > 0:

        print "this is  n=%d" % n
        time.sleep(interval)
        n -=1

p = multiprocessing.Process(target=worker,args=(3,))
m = multiprocessing.Process(target=worker,args=(4,))

p.start()
m.start()
print m.pid
print '-----------'
print p.pid
print p.name
print p.is_alive()
print multiprocessing.active_children() # 获取所有的进程
print 'main end'

 

1.2 类

import multiprocessing
import time

class ClockProcess(multiprocessing.Process):
    def __init__(self, interval):
        multiprocessing.Process.__init__(self)
        self.interval = interval

    def run(self):
        n = 5
        while n > 0:
            print("the time is {0}".format(time.ctime()))
            time.sleep(self.interval)
            n -= 1

if __name__ == '__main__':
    p = ClockProcess(3)
    p.start()    
p.daemon = True
因子进程设置了daemon属性,主进程结束,它们就随着结束了,# 默认是false
2. Lock 锁
当多个进程需要访问共享资源的时候,Lock可以用来避免访问的冲突。
import multiprocessing
import sys

def worker_with(lock, f):
    with lock:
        fs = open(f, 'a+')
        n = 10
        while n > 1:
            fs.write("Lockd acquired via with\n")
            n -= 1
        fs.close()
        
def worker_no_with(lock, f):
    lock.acquire()
    try:
        fs = open(f, 'a+')
        n = 10
        while n > 1:
            fs.write("Lock acquired directly\n")
            n -= 1
        fs.close()
    finally:
        lock.release()
    
if __name__ == "__main__":
    lock = multiprocessing.Lock()
    f = "file.txt"
    w = multiprocessing.Process(target = worker_with, args=(lock, f))
    nw = multiprocessing.Process(target = worker_no_with, args=(lock, f))
    w.start()
    nw.start()
    print "end"

 

3.Event用来实现进程间同步通信。

import multiprocessing
import time

def worker(e):
     print "worker start"
     e.wait()
     print ("workder e.is_set()" +str(e.is_set()))


def worker_for(e,t):
    print "worker_for start"
    e.wait(t)

    print "worker)for is_set" + str(e.is_set())


e = multiprocessing.Event()
w1 = multiprocessing.Process(target=worker,args=(e,))
w2 = multiprocessing.Process(target=worker_for,args=(e,4))

w1.start()
w2.start()

time.sleep(3)
e.set()
print "mail"

4.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异常。Queue的一段示例代码:
def writer_proc(q):
    try:
        q.put("this a ")
        print "put"
    except Exception as e:
        print e

def reader_proc(q):
    try:
        print "get"
        print q.get()
    except Exception as e:
        print e
        print '1111'
        pass

q = multiprocessing.Queue()
writer = multiprocessing.Process(target=writer_proc,args=(q,))
writer.start()

reader = multiprocessing.Process(target=reader_proc,args=(q,))

reader.start()

#writer.join()
#reader.join()

 

5. Pipe

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

def proc1(pipe):
    while True:
        for i in xrange(1000):
            print "send : %d" % i
            pipe.send(i)
            time.sleep(1)

def proc2(pipe):
    while True:
        print "proc2 sev:", pipe.recv()
        time.sleep(2)

def proc3(pipe):
    while True:
        print "Proc3 rev:" , pipe.recv()
        time.sleep(1)
pipe = multiprocessing.Pipe()
print pipe
p1 = multiprocessing.Process(target=proc1,args=(pipe[0],))
p2 = multiprocessing.Process(target=proc2,args=(pipe[1],))
#p3 = multiprocessing.Process(target=proc3,args=(pipe[1],))
p1.start()
p2.start()
#p3.start()
p1.join()
p2.join()
#p3.join()

 6. Pool

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

 

函数解释:

  • apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,apply(func[, args[, kwds]])是阻塞的(理解区别,看例1例2结果区别)
  • close()    关闭pool,使其不在接受新的任务。
  • terminate()    结束工作进程,不在处理未完成的任务。
  • join()    主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用。

执行说明:创建一个进程池pool,并设定进程的数量为3,xrange(4)会相继产生四个对象[0, 1, 2, 4],四个对象被提交到pool中,因pool指定进程数为3,所以0、1、2会直接送到进程中执行,当其中一个执行完事后才空出一个进程处理对象3,所以会出现输出“msg: hello 3”出现在"end"后。因为为非阻塞,主函数会自己执行自个的,不搭理进程的执行,所以运行完for循环后直接输出“mMsg: hark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~”,主程序在pool.join()处等待各个进程的结束。

三、协程

1. greenlet实现协程

  Python的 greenlet就相当于手动切换,去执行别的子程序,在“别的子程序”中又主动切换回来。。。

from greenlet import greenlet

def test1():
    print 123
    gr2.switch()
    print 456
    gr2.switch()

def test2():
    print "abc"
    gr1.switch()
    print "def"

gr1 = greenlet(test1)
gr2 = greenlet(test2)

gr1.switch()

2. gevent 实现协程

  Gevent 是一个第三方库,可以轻松通过gevent实现协程程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

  gevent会主动识别程序内部的IO操作,当子程序遇到IO后,切换到别的子程序。如果所有的子程序都进入IO,则阻塞。

 

 

参考资料:https://www.cnblogs.com/tkqasn/p/5700281.html

https://www.cnblogs.com/kaituorensheng/p/4445418.html (多进程)

https://www.cnblogs.com/zingp/p/5911537.html (协程)

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!