python多线程与多进程基础
一、python多线程的基本使用
1、多线程的调用方式:
①、直接调用:
# -*- coding:utf-8 -*- import threading import time def run(args): print args time.sleep(1) # 直接调用,target后面接线程启动的目标函数,args后面接目标函数的参数,必须以元组的形式 t = threading.Thread(target=run, args=('args', )) # 启动子线程 t.start()
②、类调用(继承调用):
# -*- coding:utf-8 -*- import threading import timedef controller(args): # 打印当前线程名 print threading.current_thread().getName() # 打印当前活动的线程数 print threading.active_count() print args time.sleep(1) # 自定义线程类,并继承threading.Thread class MyThread(threading.Thread): def __init__(self, args): # 封装调用controller函数的参数 self.args = args # 使用父类的构造方法 super(MyThread, self).__init__() # 重写线程调用时执行的run方法 def run(self): controller(self.args) print 'controller函数执行了' # 程序内打印__name__的值为__main__,程序外调用__name__的值为程序名 if __name__ == '__main__': # 实例化自定义的线程类 t = MyThread('args') # 启动线程子线程 t.start() print 'main thread running'
2、主线程的等待join方法和设置守护线程:
①、多线程的join()方法:
前面的脚本运行后,你就会发现,子线程还没有执行完,主线程已经退出了,那么就做不到线程之间的数据同步;使用join方法,让主线程等待子线程全部退出后再执行自己的代码段:
# -*- coding:utf-8 -*- import threading import time import sys def controller(args): # 打印当前线程名 print threading.current_thread().getName() # 打印当前活动的线程数 print threading.active_count() print args time.sleep(1) # 自定义线程类,并继承threading.Thread class MyThread(threading.Thread): def __init__(self, args): # 封装调用controller函数的参数 self.args = args # 使用父类的构造方法 super(MyThread, self).__init__() # 重写线程调用时执行的run方法 def run(self): controller(self.args) # 程序内打印__name__的值为__main__,程序外调用__name__的值为程序名 if __name__ == '__main__': thread_pool = [] for i in range(10): # 实例化自定义的线程类 t = MyThread('args') # 把新创建的线程加入到线程池 thread_pool.append(t) # 启动线程子线程 t.start() for thread in thread_pool: # 主线程等待所有子线程退出 thread.join() print 'main thread running'
②设置守护线程 setDaemon():
子线程设置为了守护线程就变成了主线程的仆人,主线程一停止,不管子线程是否执行完毕,都会被停止:
# -*- coding:utf-8 -*- import threading import time def run(args): print args time.sleep(3) print '设置守护线程这里将不会执行' # 直接调用,target后面接线程启动的目标函数,args后面接目标函数的参数,必须以元组的形式 t = threading.Thread(target=run, args=('args', )) # 设置守护进行,主线程执行完毕后,子线程自动退出 t.setDaemon(True) # 启动子线程 t.start() time.sleep(1) print 'main thread running...'
3、使用线程锁:
因为python多线程的全局解释器锁的存在,同一时间只有一个线程在执行,但是多个线程调用的时候,他们都会拷贝走一份数据,前一个线程未执行完,如果下一个线程就获得GIL锁的话,数据就会修改错误,所以在这里可以给每个线程在加一把锁,用于保护文件数据的修改:
# -*- coding:utf-8 -*- import threading import time def run(args): # 获得锁 # 程序执行到线程锁中间的代码段,每个线程都会等待上一个线程执行完毕 lock.acquire() print args time.sleep(3) # 释放锁 lock.release() print '设置守护线程这里将不会执行' # 生成一把线程锁 lock = threading.Lock() t = threading.Thread(target=run, args=('args', )) # 启动子线程 t.start() t.join() time.sleep(1) print 'main thread running...'
如果遇到要使用多把锁的情况,可以使用递归锁:
# -*- coding:utf-8 -*- import threading import time def run(args): # 获得锁 # 程序执行到线程锁中间的代码段,每个线程都会等待上一个线程执行完毕 lock.acquire() print args run1('run1') time.sleep(3) # 释放锁 lock.release() print '设置守护线程这里将不会执行' def run1(args): # 获得锁 # 程序执行到线程锁中间的代码段,每个线程都会等待上一个线程执行完毕 lock.acquire() print args time.sleep(3) # 释放锁 lock.release() print '设置守护线程这里将不会执行1' # 生成一把递归线程锁 lock = threading.RLock() t = threading.Thread(target=run, args=('args', )) # 启动子线程 t.start() t.join() time.sleep(1) print 'main thread running...'
4、信号量 Semaphore:
lock线程锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据;
# -*- coding:utf-8 -*- import threading import time def run(args): # 获得锁 # 程序执行到线程锁中间的代码段,每个线程都会等待上一个线程执行完毕 semaphore.acquire() print args time.sleep(3) # 释放锁 semaphore.release() # 设置信号量 semaphore = threading.BoundedSemaphore(5) for i in range(20): t = threading.Thread(target=run, args=('args', )) # 启动子线程 t.start() time.sleep(1) print 'main thread running...'
5、延时启动 Timer:
# -*- coding:utf-8 -*- import threading import time def run(args): print args time.sleep(3) for i in range(5): # 延时启动 t = threading.Timer(3.0, run, args=('args', )) # 启动子线程 t.start() time.sleep(1) print 'main thread running...'
6、Events 事件:
多线程中可以通过Event来实现两个或多个线程间的交互,通过event设置标志位,线程通过不断检测标志位是否被设定,来判断是否执行,因此可以用来调度多个线程的运行情况;
相关方法有:
# 创建一个事件的实例 event = threading.Event() event.set() # 设置标志位 event.clear() # 清除标志位 event.wait() # 等待标志位被设定 event.is_set() # 判断标志位是否被设定
实例demo:
# -*- coding:utf-8 -*- import threading import time def logcat(args): while True: if event.is_set(): print '标志位被设定,线程运行。。。' time.sleep(1) else: print '标志位未被设定,等待中。。。' # 未检测到标志位,进行阻塞 event.wait() # 创建一个函数,用于控制标志位 def run(): event.set() count = 0 while True: if 5 < count < 10: event.clear() elif count > 10: event.set() count = 0 count += 1 time.sleep(1) # 创建一个事件的实例 event = threading.Event() t1 = threading.Thread(target=run,) t2 = threading.Thread(target=logcat, args=('adb devices',)) t1.start() t2.start()
二、queue队列
使用队列,进行线程间的通信;
1、实例化一个队列,并指定队列的长度:
# -*- coding:utf-8 -*- import Queue # 实例化一个队列,接受参数为队列的最大长度 q = Queue.Queue(maxsize=5)
2、往队列中添加数据:
q.put(1) # 往队列中put数据,超过最大长度,则会阻塞,直到有数据被取出
3、从队列中取出数据:
q.get() # 取出一条数据
4、查看队列的状态:
q.full() # 判断队列是否已满,返回布尔值 q.empty() # 判断队列是否为空,返回布尔值 q.qsize() # 返回队列的长度
5、设置不同规则队列的:
①先入先出队列:
②、后入先出队列:
③、自定义出列顺序:
三、多进程的基本使用
1、创建进程:
# -*- coding:utf-8 -*- from multiprocessing import Process import time import os def run(): print 'process running' # 打印当前进程的进程号 print os.getpid() time.sleep(2) # windows启动多进程需要加下面这行 if __name__ == '__main__': # 创建一个进程 p = Process(target=run,) p.start() p.join() print 'main process running '
2、进程间通讯:
①、进程的队列Queue:
# -*- coding:utf-8 -*- from multiprocessing import Process, Queue import time def run(q): q.put(23) time.sleep(2) if __name__ == '__main__': # 创建一个进程Q q = Queue(maxsize=5) '''通过参数进行传递队列,因为不同进程间的内存地址不相同, 因此不能同时共享一份数据,这里是将创建的队列先序列化,传过去,再序列化传回来''' p = Process(target=run, args=(q, )) p.start() print q.get()
②、管道Pipe:
通过类似socket的方式进行数据的传递:
# -*- coding:utf-8 -*- from multiprocessing import Process, Pipe def run(p): text = p.recv() print text p.send('hello, father') if __name__ == '__main__': # 新建一个管道,获得管道的两端,一端给子进程,一端父进程拿着 father, son = Pipe() p = Process(target=run, args=(son, )) p.start() # 通过send和recv来接收,发送数据 father.send('hello, son') print father.recv()
③、Managers 数据类型管理:
支持的数据类型: list
, dict
, Namespace
, Lock
, RLock
, Semaphore
, BoundedSemaphore
, Condition
, Event
, Barrier
, Queue
, Value
and Array
.
# -*- coding:utf-8 -*- from multiprocessing import Process, Manager def run(l1, d1): l1.append(10) d1['12'] = '23' if __name__ == '__main__': # 创建一个进程间可以数据共享的对象 manager = Manager() # 生成一个进程间可以数据共享的列表 list1 = manager.list() # 生成一个进程间可以数据共享的字典 dict1 = manager.dict() p = Process(target=run, args=(list1, dict1)) p.start() p.join() print list1, dict1
3、进程池:
因为在windows上开多了进程,系统会爆炸的,所以需要有个进程池;
# -*- coding:utf-8 -*- from multiprocessing import Process, Pool import time def f(i): time.sleep(1) return i # 回调函数 def run(arg): print('-->callback:', arg) if __name__ == '__main__': pool = Pool(5) for i in range(10): # 进程池,并可以指定父进程执行的回调函数 pool.apply_async(func=f, args=(i,), callback=run) pool.close() pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。 print('process run end')