python--多线程:锁 、全局锁、Queue队列以及线程池

关于如何加锁,获取钥匙,释放锁:

  • lock = threading.Lock():生成锁对象,全局唯一;
  • lock.acquire():获取锁。未获取到会阻塞程序,直到获取到锁才会往下执行;
  • lock.release():释放锁,归回后,其他人也可以调用;

  【注意事项】:lock.acquire() 和 lock.release()必须成对出现,否则就有可能造成死锁。

  为了规避这个问题,可以使用使用上下文管理器来加锁。如下所示:

  

import threading
lock = threading.Lock()
with lock:
    # 这里写想要实现的代码
    pass

关于GIL(全局锁):

  任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

  【重点】python解释器中任意时刻都只有一个线程在执行;

关于Queue队列

  谈及多线程,就不得不说Queue队列,这是从一个线程向另一个线程发送数据最安全的方式。创建一个被多个线程共享的 Queue 对象,这些线程通过使用put() 和 get() 操作来向队列中添加或者删除元素。

from queue import Queue
# maxsize默认为0,不受限
# 一旦>0,而消息数又达到限制,q.put()也将阻塞
q = Queue(maxsize=0)

# 阻塞程序,等待队列消息。
q.get()

# 获取消息,设置超时时间
q.get(timeout=5.0)

# 发送消息
q.put()

# 等待所有的消息都被消费完
q.join()

# 以下三个方法,知道就好,代码中不要使用

# 查询当前队列的消息个数
q.qsize()

# 队列消息是否都被消费完,True/False
q.empty()

# 检测队列里消息是否已满
q.full()

关于线程池:

在Python3中,创建线程池是通过concurrent.futures函数库中的ThreadPoolExecutor类来实现的。

import Queue
import threading
import time

'''
这个简单的例子的想法是通过:
1、利用Queue特性,在Queue里创建多个线程对象
2、那我执行代码的时候,去queue里去拿线程!
如果线程池里有可用的,直接拿。
如果线程池里没有可用,那就等。
3、线程执行完毕,归还给线程池
'''

class ThreadPool(object): #创建线程池类
    def __init__(self,max_thread=20):#构造方法,设置最大的线程数为20
        self.queue = Queue.Queue(max_thread) #创建一个队列
        for i in xrange(max_thread):#循环把线程对象加入到队列中
            self.queue.put(threading.Thread)
            #把线程的类名放进去,执行完这个Queue

    def get_thread(self):#定义方法从队列里获取线程
        return self.queue.get()

    def add_thread(self):#定义方法在队列里添加线程
        self.queue.put(threading.Thread)

pool = ThreadPool(10)

def func(arg,p):
    print arg
    time.sleep(2)
    p.add_thread() #当前线程执行完了,我在队列里加一个线程!

for i in xrange(300):
    thread = pool.get_thread() #线程池10个线程,每一次循环拿走一个!默认queue.get(),如果队列里没有数据就会等待。
    t = thread(target=func,args=(i,pool))
    t.start()

关于回调函数add_done_callback

回调函数是在调用线程完成后再调用的,在同一个线程中。

import requests
import os
import random
import concurrent.futures as futures

def download_img(url):
    resp = requests.get(url)
    filename = os.path.split(url)[1] # 获取文件名
    with open(filename,'wb+') as f:
        f.write(resp.content)
    num = random.randint(2,5)
    print(filename + "generate:",num)
    time.sleep(num)
    return filename

urls = ["http://pic27.nipic.com/20130320/8952533_092547846000_2.jpg",
        "http://pic19.nipic.com/20120212/9337475_104548381000_2.jpg",]

ex = futures.ThreadPoolExecutor(max_workers = 3)
res_iter = ex.map(download_img,urls)
type(res_iter)
for res in res_iter:
    print(res)

def cf(rs):
    print(rs.result())

# [ex.submit(download_img,url).add_done_callback(cf) for url in urls]
# for future in futures.as_completed(fu_tasks):
for url in urls:
    f = ex.submit(download_img,url)
    f.add_done_callback(cf)

 

posted @ 2023-04-23 11:35  hotzhml  阅读(419)  评论(0编辑  收藏  举报