python线程

  • 子类继承父类的初始化方法
class student1(student):      #student为父类,父类有name,age,stno这些属性
    def __init__(self,name,age,stno,addr):    #在父类的基础上加了一个addr属性
        student.__init(self,name,age,stno)    #显示调用父类的初始化方法
        self.addr=addr       #不在父类的属性自行初始化
class myThread(threading.Thread):      #继承父类Thread
    def __init__(self,url):
        super().__init__()     #通过super()关键字显示调用父类
        self.url=url

创建线程的两种方法

  • 通过Thread类构造器来创建线程
def get_web(url):      #定义创建线程之后要执行的方法,即是线程的执行体
    headers = {
        'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36'
    }
    req = urllib.request.Request(url,headers=headers)
    resp = urllib.request.urlopen(req)
    print(resp.read().decode()[:50])

if __name__=='__main__':
    t=threading.Thread(target=get_web,args=(url,))    #使用Thread类的构造器来创建线程,在线程参数中规定线程的执行方法和方法的参数
    t.start()  #运行线程
    t.join()   #阻塞线程,执行主程序
  • 通过继承Thread类创建线程
class myThread(threading.Thread)       #定义类来继承Thread,创建新线程
    def __init__(self,url):      #定义新类的初始化方法
        super().__init__()       #显示调用父类的初始化方法
        self.url=url
    def run(self)              #定义父类中的run方法,表示线程的执行过程
        headers = {
            'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36'
        }
    req = urllib.request.Request(self.url,headers=headers)
    resp = urllib.request.urlopen(req)
    print(resp.read().decode()[:50])

if __name__=='__main__':
    t=myThread(url)      #初始化新定义的线程,传递新线程所需的各种参数
    t.start()     #运行线程
    t.join()      #阻塞主程序,执行线程

全局解释器锁GIL。当前线程执行I/O操作时,会释放GIL。当前线程执行超过100字节码,也会释放GIL。

import threading
n = 0
lock = threading.Lock()
def add():
    for i in range(1000000):
        with lock:
            global n
            n += 1
def minus():
    for i in range(1000000):
        with lock:
            global n
            n -= 1
if __name__=='__main__':
    t1 = threading.Thread(target=add)
    t2 = threading.Thread(target=minus)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(n)       #最后执行结果还是0,加入了锁 

使用Queue,消息队列来实现线程间的通信

import threading
import time
from queue import Queue
def produce(q):
    kind=('猪肉','白菜','豆沙')
    for i in range(3):
        print(threading.current_thread().name,'包子生产者开始生产包子')
        time.sleep(1)
        q.put(kind[i%3])     #生产者生产后,放入一个包子到消息队列
        print(threading.current_thread().name,'包子生产者生产完包子')
def consume(q):
    while True:
        print(threading.current_thread().name,'消费者准备吃包子')
        time.sleep(1)
        t=q.get()      #get方法是一个阻塞方法,如果消息队列中没有包子,就会阻塞当前线程
        print('消费者吃了{}包子'.format(t))
if __name__=='__main__':
    q=Queue(maxsize=1)
    threading.Thread(target=produce,args=(q,)).start()  #启动两个生产者线程
    threading.Thread(target=produce,args=(q,)).start()
    threading.Thread(target=consume,args=(q,)).start()  #启动一个消费者线程

#执行流程
#先启动三个线程分别执行到线程中的一部分
   # 包子生产者1开始生产包子
   # 包子生产者2开始生产包子
   # 消费者准备吃包子
#此时消息队列中没有包子,生产者1执行剩余部分后,又循环回到头部重新生产包子
   # 包子生产者1生产完包子
   # 包子生产者1开始生产包子
#此时,消息队列中有包子,消费者开始消费
   # 消费者吃了什么包子
#消费者消费完包子后,消息队列中没有包子,生产者2开始生产包子
   # 包子生产者2生产完包子
   # 包子生产者2开始生产包子
#生产者2生产包子后,消息队列中有包子,消费者吃完包子后又回到循环等待包子
   # 消费者吃了什么包子
   # 消费者准备吃包子
#此后,生产者1又重新开始生产包子,消费者消费包子,生产者2开始生产包子,消费者消费包子。直到两个生产者都生产完3个包子后,消息队列中没有包子,结束执行。

使用Event事件对象让线程通信。适合与集合点的测试,线程启动后,不是立刻执行,而是等待所有线程都启动后,测试系统的并发执行效率

import threading
import time
class myThread(threading.Thread):          #自定义线程对象
    def __init__(self,event):
        super().__init__()                 #继承父类初始化方法
        self.event=event
    def run(self):               #自定义run方法
        print('线程{}已经启动\n'.format(self.name))
        self.event.wait()       #启动线程后,使用wait方法阻塞所有线程,等待event指令
        print('线程{}开始运行'.format(self.name))
if __name__=='__main__':
    event=threading.Event()          #创建Event对象
    threads=[]           #定义线程列表
    [threads.append(myThread(event) for i in range(1,11))]  #创建10个线程,放入到threads列表中
    event.clear()        #创建线程后,使所有Event对象都处于待命状态
    [t.start() for t in threads]        #执行线程
    event.set()           #执行每个线程时,都会进入等待Event对象发送指令状态,需要set方法发送指令
    [t.join() for t in threads]       #使每个线程和主线程分割

在多个线程之间交替执行或一个线程在等待其他线程时,使用Condition条件对象就很合适

import threading
cond=threading.Condition()      #定义Condition对象
class k(threading.Thread):      #定义一个要通信的对象
    def __init__(self,cond,name):
        threading.Thread.__init__(self,name=name)
        self.cond=cond
    def run(self):                     #定义线程执行体
        self.cond.acquire()          #线程k获得锁
        print(self.getName() +':一支穿云箭')
        self.cond.notify()          #线程k发送消息后,通知线程x接收消息
        self.cond.wait()              #线程k通知x后,等待x的消息
        print(self.getName()+':山无棱,天地合,乃敢与君决')
    self.cond.notify()
    self.cond.wait()
    print(self.getName()+':紫薇')
    self.cond.notify()
    self.cond.wait()
    print(self.getName()+':是你')
    self.cond.notify()
    self.cond.wait()
        print(self.getName()+':借点钱')
        self.cond.notify()                #线程k与线程x之间最后一次通信,通知x后,释放锁
        self.cond.release()        #线程k释放锁
class x(threading.Thread):         #定义另一个要通信的线程
    def __init__(self,cond,name):   #定义线程x的初始化方法
        threading.Thread.__init__(self,name=name)
        self.cond=cond
    def run(self):                   #定义线程x的执行体
        self.cond.acquire()        #线程x获得锁
        self.cond.wait()             #线程x等待线程k发送消息
        print(self.getName()+':千军万马来相见')
    self.cond.notify()
    self.cond.wait()
    print(self.getName()+':海可枯,石可烂,激情永不散')
    self.cond.notify()
    self.cond.wait()
    print(self.getName()+':尔康')
    self.cond.notify()
    self.cond.wait()
    print(self.getName()+':是我')
    self.cond.notify()
    self.cond.wait()
        print(self.getName()+':滚')
        self.cond.release()        #线程x在发送完最后一条信息后,释放锁
if __name__ =='__main__':
    k = kongbeige(cond,'空白哥')
    x = ximige(cond,'西米哥')
    x.start()
    k.start()

执行过程

线程之间的消息隔离。定义一个全局变量,在每个子线程中都会使用到。比如网络开发中,每个用户都有一个session,使用线程之间的全局变量可以让每个用户管理自己的session对象。在线程类之中的上下文管理中,数据库连接中,都能够使用到消息隔离。

import threading
local_data=threading.local()        #定义一个全局变量
local_data.name='local_data'
class localThread(threading.Thread):        #定义子线程
    def run(self):
        print('赋值前,主线程',threading.current_thread(),local_data.__dict__)
        local_data.name=self.getName()       #在子线程中修改全局变量
        print('赋值后,子线程',threading.current_thread(),local_data.__dict__)
if __name__=='__main__':
    print('赋值前,主线程',threading.current_thread,local_data.__dict__)     #定义主线程,和子线程比较,观察全局变量是否改变
    t1 = localThread()
    t1.start()
    t1.join()
    t2 = localThread()
    t2.start()
    t2.join()
    print('赋值后,主线程',threading.current_thread,local_data.__dict__)

最后的输出结果说明进程内部的全局变量,在主线程中没有发生改变,在两个子线程中分别被修改。

为了降低创建线程造成的资源消耗,引入了线程池,在线程池中的线程执行结束后,会被回收,下一个相同的任务到来,线程会直接执行。

线程池中,主线程会获取某个线程的任务或状态,以及返回值。某个子线程执行结束后,主线程立刻知道,会回收。

from concurrent.futures import ThreadPoolExecutor
import time 
executor=ThreadingPoolExecutor(max_workers=3)     #定义一个线程池对象,最大只允许三个线程
def get_html(times):         #定义线程池中子线程的执行过程
    time.sleep(times)
    print('获取网页{}信息'.format(times))
    return times
task1=executor.submit(get_html,1)          #把第一个线程装入线程池,只需要把子线程的方法名传入,后面跟上方法中的参数即可。
task2=executor.submit(get_html,2)
task3=executor.submit(get_html,3)

在线程池中提交了线程后,可以通过提交后的函数句柄来查看线程的执行状况。

print(task1.done())         #查看线程1是否执行结束,一般在将线程提交后不会立即执行,会返回false值。
#在上面代码之前添加time.sleep(3)后,即在主线程中添加时间延迟后,线程会执行,此时查看线程状态,会返回true值
print(task2.cancel())       #查看线程2是否撤销,一般在线程没有提交到线程池,才能撤销线程
print(task1.result())        #拿到线程执行的结果,这是一个阻塞方法

在线程池中的线程执行结束后,如何查看运行结果。Python提供task.done()方法获取线程执行情况。但是这个方法在线程还没执行结束时,无法查看线程执行过程。所以有as_completed,map,wait方法可以在线程执行结束后,查看线程的状态。

from concurrent.futures import ThreadPoolExecutor,as_completed,ALL_COMPLETED
urls=[1,2,3,4]     #定义需要抓取的地址
def get_web():    #定义线程的执行过程
    pass
all_tasks=[executor.submit(get_web,url) for url in urls]     #定义要执行的任务队列
for item in as_completed(all_tasks):           #使用as_completed查看线程的执行情况
    data=item.result()
    print('主线程获取任务的返回值{}'.format(data))
#使用as_completed是按照线程的执行情况来先后输出线程状态
#使用map只是按照线程的装入顺序来输出线程执行情况
for data in executor.map(get_web,urls):      #定义map方法获取线程执行的状态
    print('主线程获取任务的返回值{}'.format(data))
#使用wait方法即是在所有子线程结束后,再去运行主线程
wait(all_tasks,return_when=ALL_COMPLETED)    #参数ALL_COMPLETED就是所有子线程执行结束后,再执行主线程

多进程的实现和多线程大体相同。

多线程具有效率高,耗费资源少,所以非常适合I/O密集型的作业,比如文件读取,爬虫程序。这些程序主要的时间消耗集中在等待时间,多线程就非常适合。多线程缺点就是稳定性差,一个线程崩溃,其余线程也会受到影响。

多进程稳定性高,进程之间互不干扰。所以非常适合计算密集型的作业。多进程缺点耗费计算机大量资源。

posted @ 2020-04-04 17:19  岐岐卡卡西  阅读(206)  评论(0编辑  收藏  举报