多线程
多进程可以同时执行多任务,而也可以通过多线程来完成,一个进程至少有一个线程。
-
Python中提供了threading模块来对多线程的操作
- 启动方法:就是把一个函数传入并创建Thread实例,然后调用start()开始执行:
- 上述多进程的有个小例子修改一下:
#!/usr/bin/env python import multiprocessing import threading import time def func(msg): print("*******start******{}".format(msg)) time.sleep(3) print("*******end******{}".format(msg)) if __name__ == '__main__': # n = multiprocessing.cpu_count() # 调用多进程的方法,注释掉 # pool = multiprocessing.Pool(processes=n) for i in range(1,6): msg = 'hello,{}'.format(i) # pool.apply_async(func,(msg,)) # 多进程方法,注意对比 t = threading.Thread(target=func,args=(msg,)) 实例化一个多线程 t.start() # print("*******start main*********") # pool.close() # pool.join()
多线程调用方法和多进程:
t = threading.Thread(target=action, args=(i,)) -
线程锁Lock
- 先看一个例子:
import time, threading balance = 0 def change_it(n): global balance balance = balance + n balance = balance - n def run_thread(n): for i in range(100000): change_it(n) t1 = threading.Thread(target=run_thread, args=(5,)) t2 = threading.Thread(target=run_thread, args=(8,)) t1.start() t2.start() t1.join() t2.join() print(balance)
理论上,balance的值为零,但是,由于线程的调度是由操作系统决定的,当t1、t2交替执行时,只要循环次数足够多,balance的结果就不一定是0了。这是由于多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
- 如果我们要确保balance计算正确,就要给change_it()上一把锁,当某个线程开始执行change_it()时,我们说,该线程因为获得了锁,因此其他线程不能同时执行change_it(),只能等待,直到锁被释放后,获得该锁以后才能改。由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。创建一个锁就是通过threading.Lock()来实现:
import time, threading lock = threading.Lock() # 实例化一个lock balance = 0 def change_it(n): global balance balance = balance + n balance = balance - n def run_thread(n): for i in range(1000000): lock.acquire() # 获取锁,使其在被调用时其它进程无法调用它 try: change_it(n) finally: lock.release() # 最后要释放锁 t1 = threading.Thread(target=run_thread, args=(5,)) t2 = threading.Thread(target=run_thread, args=(8,)) t1.start() t2.start() t1.join() t2.join() print(balance)
-
线程池
- 通过传入一个参数组来实现多线程,并且它的多线程是有序的,顺序与参数组中的参数顺序保持一致。
- 安装方法:
pip install threadpool
- 调用方法:
pool = ThreadPool(poolsize) requests = makeRequests(some_callable, list_of_args, callback) [pool.putRequest(req) for req in requests] pool.wait()
第一行定义了一个线程池,表示最多可以创建poolsize这么多线程;
第二行是调用makeRequests创建了要开启多线程的函数,以及函数相关参数和回调函数,其中回调函数可以不写,default是无,也就是说makeRequests只需要2个参数就可以运行;
第三行用法是将所有要运行多线程的请求扔进线程池,[pool.putRequest(req) for req in requests]
第四行是等待所有的线程完成工作后退出。- 例子:
#!/usr/bin/env python # -*- coding:utf-8 -*- import threadpool def hello(m, n, o): print "m = %s, n = %s, o = %s" % (m, n, o) if __name__ == '__main__': # 方法1 lst_vars_1 = ['1', '2', '3'] lst_vars_2 = ['4', '5', '6'] func_var = [(lst_vars_1, None), (lst_vars_2, None)] # 方法2 dict_vars_1 = {'m': '1', 'n': '2', 'o': '3'} dict_vars_2 = {'m': '4', 'n': '5', 'o': '6'} func_var = [(None, dict_vars_1), (None, dict_vars_2)] pool = threadpool.ThreadPool(2) requests = threadpool.makeRequests(hello, func_var) [pool.putRequest(req) for req in requests] pool.wait()