multiprocessing
multiprocessing 是一个用与 threading 模块相似API的支持产生进程的包。 multiprocessing 包同时提供本地和远程并发,使用子进程代替线程,有效避免 Global Interpreter Lock 带来的影响。因此, multiprocessing 模块允许程序员充分利用机器上的多个核心。Unix 和 Windows 上都可以运行。
multiprocessing 模块还引入了在 threading 模块中没有类似物的API。这方面的一个主要例子是 Pool 对象,它提供了一种方便的方法,可以跨多个输入值并行化函数的执行,跨进程分配输入数据(数据并行)。以下示例演示了在模块中定义此类函数的常见做法,以便子进程可以成功导入该模块。这个数据并行的基本例子使用 Pool
from multiprocessing import Pooldef f(x):return x*xif __name__ == '__main__':with Pool(5) as p:print(p.map(f, [1, 2, 3]))>>
threading
threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
- threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
- run(): 用以表示线程活动的方法。
- start():启动线程活动。
join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
import threadingdef action(*add):for arc in add:print(threading.currentThread().getName()+' '+arc)my_tuple = ('tuple_1','tuple_2','tuple_3')# 创建线程thread=threading.Thread(target=action,args=my_tuple)thread.start()thread.join()for i in range(5):print(threading.currentThread().getName())>>>>#action执行完成退出后才执行主线程Thread-9 tuple_1Thread-9 tuple_2Thread-9 tuple_3MainThreadMainThreadMainThreadMainThreadMainThread
isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
- daemon(): 一个表示这个线程是(True)否(False)守护线程的布尔值。一定要在调用 start() 前设置好,不然会抛出 RuntimeError 。初始值继承于创建线程;主线程不是守护线程,因此主线程创建的所有线程默认都是 daemon = False。
创建线程
import threadingimport timeflag = 0class myThread(threading.Thread):def __init__(self, threadID, name, delay):threading.Thread.__init__(self)self.threadID = threadIDself.name = nameself.delay = delaydef run(self):print ("开始线程:" + self.name)print_time(self.name, self.delay, 5)print ("退出线程:" + self.name)def print_time(threadName, delay, counter):while counter:if flag:threadName.exit()time.sleep(delay)print ("%s: %s" % (threadName, time.ctime()))counter -= 1thread1 = myThread(1,'Thread-1',1)thread2 = myThread(1,'Thread-1',2)thread1.start()thread2.start()thread1.join()thread2.join()print('退出主线程')>>>>开始线程:Thread-1开始线程:Thread-2Thread-1: Thu Jan 20 16:41:19 2022Thread-1: Thu Jan 20 16:41:20 2022Thread-2: Thu Jan 20 16:41:20 2022Thread-1: Thu Jan 20 16:41:21 2022Thread-2: Thu Jan 20 16:41:22 2022Thread-1: Thu Jan 20 16:41:22 2022Thread-1: Thu Jan 20 16:41:23 2022退出线程:Thread-1Thread-2: Thu Jan 20 16:41:24 2022Thread-2: Thu Jan 20 16:41:26 2022Thread-2: Thu Jan 20 16:41:28 2022退出线程:Thread-2退出主线程
向线程中传递函数
创建类的实例,使用args函数
import threadingdef func1(s,func):fun(s)def ff(s):print(f'输出了{s}')t1 = threading.Thread(target=func1,args=('hello world',ff))t1.start()
线程取得返回值
import threadingclass MyThread(threading.Thread):def __init__(self,func,args=()):super(MyThread,self).__init__()self.func=funcself.args=argsdef run(self):self.result = self.func(*self.args)def get_return(self):threading.Thread.join(self)try:return self.resultexcept Exception as e:return None
线程中创建和使用全局对象
使用threading.local()来定义
import threadingimport timea = threading.local()def worker():a.x = 0for i in range(20):a.x +=1print(threading.current_thread(),a.x)for i in range(10):threading.Thread(target=worker).start()
线程同步
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。如下:
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。
考虑这样一种情况:一个列表里所有元素都是0,线程”set”从后向前把所有元素改成1,而线程”print”负责从前往后读取列表并打印。
那么,可能线程”set”开始改的时候,线程”print”便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。
锁有两种状态——锁定和未锁定。每当一个线程比如”set”要访问共享数据时,必须先获得锁定;如果已经有别的线程比如”print”获得锁定了,那么就让线程”set”暂停,也就是同步阻塞;等到线程”print”访问完毕,释放锁以后,再让线程”set”继续。
经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。
import threadingimport timeclass myThread (threading.Thread):def __init__(self, threadID, name, delay):threading.Thread.__init__(self)self.threadID = threadIDself.name = nameself.delay = delaydef run(self):print ("开启线程: " + self.name)# 获取锁,用于线程同步threadLock.acquire()print_time(self.name, self.delay, 3)# 释放锁,开启下一个线程threadLock.release()def print_time(threadName, delay, counter):while counter:time.sleep(delay)print ("%s: %s" % (threadName, time.ctime(time.time())))counter -= 1#创建threading的Lock()方法对象threadLock = threading.Lock()threads = []# 创建新线程thread1 = myThread(1, "Thread-1", 1)thread2 = myThread(2, "Thread-2", 2)# 开启新线程thread1.start()thread2.start()# 添加线程到线程列表threads.append(thread1)threads.append(thread2)# 等待所有线程完成,Thread-1完成后才会开始Thread-2for t in threads:t.join()print ("退出主线程")>>>>开启线程: Thread-1开启线程: Thread-2Thread-1: Thu Jan 20 16:40:03 2022Thread-1: Thu Jan 20 16:40:04 2022Thread-1: Thu Jan 20 16:40:05 2022Thread-2: Thu Jan 20 16:40:07 2022Thread-2: Thu Jan 20 16:40:09 2022Thread-2: Thu Jan 20 16:40:11 2022退出主线程
线程锁仅仅用来确保线程不会被打断,但是无法限制进程访问的个数,所有就需要引入一个信号量的概念,用来控制线程访问和释放的次数
信号量是一个计数器,当资源释放时,计数器就递增,当资源申请时,计数器就递减
from threading import BoundedSemaphoreMAX = 10semaphore = BoundedSemaphore(MAX)print(semaphore._value)semaphore.acquire()print(semaphore._value)semaphore.release()print(semaphore._value)>>>>10910
当semaphore._value为0 时,表示资源消耗殆尽,再次调用acquire()方法会阻塞
多线程下载图片
import urllib3import threadinghttp = urllib3.PoolManager()disable_warnings()f = open('url.txt','r')urllist = []for line in f.readlines():urllist.append(line.rstrip())class Download(threading.Thread):def __init__(self,func,args):super().__init__(target=func,args=args)def download(filename,url):response = http.request('GET',url)f = open(filename,'wb')f.write(response.data)f.close()print('<%s>下载完成'%url)for i in range(len(urllist)):thread = Download(download,(str(i)+'.jpg',urllist[i]))thread.start()
线程优先级队列
Python 的 Queue 模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列 PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。
Queue 模块中的常用方法:
- Queue.qsize() 返回队列的大小
- Queue.empty() 如果队列为空,返回True,反之False
- Queue.full() 如果队列满了,返回True,反之False
- Queue.full 与 maxsize 大小对应
- Queue.get([block[, timeout]])获取队列,timeout等待时间
- Queue.get_nowait() 相当Queue.get(False)
- Queue.put(item) 写入队列,timeout等待时间
- Queue.put_nowait(item) 相当Queue.put(item, False)
- Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
- Queue.join() 实际上意味着等到队列为空,再执行别的操作
```python
import queue
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def init(self, threadID, name, q):
def run(self):threading.Thread.__init__(self)self.threadID = threadIDself.name = nameself.q = q
print ("开启线程:" + self.name)process_data(self.name, self.q)print ("退出线程:" + self.name)
def process_data(threadName, q): while not exitFlag: queueLock.acquire() if not workQueue.empty(): data = q.get() print (“%s processing %s” % (threadName, data)) queueLock.release() else: queueLock.release() time.sleep(1)
threadList = [“Thread-1”, “Thread-2”, “Thread-3”] nameList = [“One”, “Two”, “Three”, “Four”, “Five”] queueLock = threading.Lock()
先进先出队列
workQueue = queue.Queue(10) threads = [] threadID = 1
创建新线程
for tName in threadList: thread = myThread(threadID, tName, workQueue) thread.start() threads.append(thread) threadID += 1
for word in nameList: workQueue.put(word)
等待队列清空
while not workQueue.empty(): pass
通知线程是时候退出
exitFlag = 1
等待所有线程完成
for t in threads: t.join() print (“退出主线程”)
> 开启线程:Thread-1 开启线程:Thread-2 开启线程:Thread-3 Thread-3 processing One Thread-1 processing Two Thread-2 processing Three Thread-3 processing Four Thread-1 processing Five 退出线程:Thread-1 退出线程:Thread-2 退出线程:Thread-3 退出主线程 ```

