• 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('线程{}已经启动
    '.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密集型的作业,比如文件读取,爬虫程序。这些程序主要的时间消耗集中在等待时间,多线程就非常适合。多线程缺点就是稳定性差,一个线程崩溃,其余线程也会受到影响。

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

  • 相关阅读:
    2017 业余程序员的回顾
    而立将近,可有不惑
    谈谈转行
    吃干抹净提上裤子就甩
    Autosizer应用程序窗口控制工具
    AutoCAD2007专业版
    Revit中如何添加水平仰视平面视图
    Revit中绘制带坡度管道
    Revit自定义快递访问工具栏
    Revit利用对正工具快速修改风管对齐方式
  • 原文地址:https://www.cnblogs.com/feng1014/p/12632710.html
Copyright © 2020-2023  润新知