• python多线程


    在介绍Python中的线程之前,先明确一个问题,Python中的多线程是假的多线程!
    为什么这么说,我们先明确一个概念,全局解释器锁(GIL)

    什么是GIL

    Python代码的执行由Python虚拟机(解释器)来控制,同时只有一个线程在执行。对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。

    为什么要GIL

    为了线程间数据的一致性和状态同步的完整性,(例如:线程2需要线程1执行完成的结果,然而线程2又比线程1执行时间短,线程2执行完成,线程1仍然还在执行,这就是数据的同步性)

    GIL的影响

    只有一个线程在运行,无法使用多核。

    • 在多线程环境中,Python虚拟机按照以下方式执行。

      1.设置GIL。
      2.切换到一个线程去执行。
      3.运行。
      4.把线程设置为睡眠状态。
      5.解锁GIL。
      6.再次重复以上步骤。
      比方我有一个4核的CPU,那么这样一来,在单位时间内每个核只能跑一个线程,然后时间片轮转切换。
      但是Python不一样,它不管你有几个核,单位时间多个核只能跑一个线程,然后时间片轮转。
      执行一段时间后让出,多线程在Python中只能交替执,10核也只能用到1个核
      例如:

    from threading import Thread
    def loop():
        while True:
            print("亲爱的,我错了,我能吃饭了吗?")
    
    if __name__ == '__main__':
    
        for i in range(3):
            t = Thread(target=loop)
            t.start()
    
        while True:
            pass
    

    而如果我们变成进程呢?cpu --100%

    from multiprocessing import Process
    def loop():
        while True:
            print("亲爱的,我错了,我能吃饭了吗?")
    
    if __name__ == '__main__':
    
        for i in range(3):
            t = Process(target=loop)
            t.start()
    
        while True:
            pass
    

    多线程怎么使用多核

    • 1、重写python编译器(官方cpython)如使用:PyPy解释器
    • 2、调用C语言的链接库

    cpu密集型(计算密集型)、I/O密集型

    • 计算密集型任务由于主要消耗CPU资源,代码运行效率至关重要,C语言编写
    • IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成99%的时间花费在IO上,脚本语言是首选,C语言最差。

    2、创建多线程

    def doSth(arg):
        # 拿到当前线程的名称和线程号id
        threadName = threading.current_thread().getName()
        tid = threading.current_thread().ident
        for i in range(5):
            print("%s *%d @%s,tid=%d" % (arg, i, threadName, tid))
            time.sleep(2)
    

    1、使用_thread.start_new_thread开辟子线程

    def simpleThread():
        # 创建子线程,执行doSth
        # 用这种方式创建的线程为【守护线程】(主线程死去“护卫”也随“主公”而去)
        _thread.start_new_thread(doSth, ("拍森",))
    
        mainThreadName = threading.current_thread().getName()
        print(threading.current_thread())
        # 5秒的时间以内,能看到主线程和子线程在并发打印
        for i in range(5):
            print("劳资是主线程@%s" % (mainThreadName))
            time.sleep(1)
    
        # 阻塞主线程,以使【守护线程】能够执行完毕
        while True:
            pass
    

    2、 通过创建threading.Thread对象实现子线程

    def threadingThread():
        # 默认不是【守护线程】
        t = threading.Thread(target=doSth, args=("大王派我来巡山",)) # args=(,) 必须是元组
        # t.setDaemon(True)  # 设置为守护线程
        t.start()  # 启动线程,调用run()方法
        t.join()  # 等待
    

    3、通过继承threading.Thread类,进而创建对象实现子线程

    class MyThread(threading.Thread):
        def __init__(self, name, task, subtask):
            super().__init__()
    
            self.name = name  # 覆盖了父类的name
            self.task = task  # MyThread自己的属性
            self.subtask = subtask
    
        # 覆写父类的run方法,
        # run方法以内为【要跑在子线程内的业务逻辑】(thread.start()会触发的业务逻辑)
        def run(self):
            for i in range(5):
                print("[%s]并[%s] *%d @%s" % (self.task, self.subtask, i, threading.current_thread().getName()))
                time.sleep(2)
    
    
    def classThread():
        mt = MyThread("小分队I", "巡山", "扫黄")
        mt.start()  #  启动线程
    
    

    4、几个重要的API

    并行 : 多个任务同时进行,但python多线程不允许,多进程是允许的

    并发 : 多个任务在单个CPU交替执行 ,

    串行 : 任务在CPU之间快速切换 , 交替执行

    def importantAPI():
        print(threading.currentThread())  # 返回当前的线程变量
        # 创建五条子线程
        t1 = threading.Thread(target=doSth, args=("巡山",))
        t2 = threading.Thread(target=doSth, args=("巡水",))
        t3 = threading.Thread(target=doSth, args=("巡鸟",))
    
        t1.start()  # 开启线程
        t2.start()
        t3.start()
    
        print(t1.isAlive())  # 返回线程是否活动的
        print(t2.isDaemon())  # 是否是守护线程
        print(t3.getName())  # 返回线程名
        t3.setName("巡鸟")  # 设置线程名
        print(t3.getName())
        print(t3.ident)  # 返回线程号
    
        # 返回一个包含正在运行的线程的list
        tlist = threading.enumerate()
        print("当前活动线程:", tlist)
    
        # 返回正在运行的线程数量(在数值上等于len(tlist))
        count = threading.active_count()
        print("当前活动线程有%d条" % (count))
    
    

    3、线程冲突

    '''
    【线程冲突】示例:
    多个线程并发访问同一个变量而互相干扰
    互斥锁
        状态:锁定/非锁定
        #创建锁
            lock = threading.Lock()
        #锁定
            lock.acquire()
        #释放
            lock.release()
    '''
    '''
    互相锁住对方线程需要的资源,造成死锁局面
    递归锁,用于解决死锁的问题,可重复锁
    '''
    import threading
    import time
    money = 0
    
    # CPU分配的时间片不足以完成一百万次加法运算,
    # 因此结果还没有被保存到内存中就被其它线程所打断
    def addMoney():
        global money
        for i in range(1000000):
            money += 1
        print(money)
    
    # 创建线程锁
    lock = threading.Lock()
    
    def addMoneyWithLock():
        # print("addMoneyWithLock")
        time.sleep(1)
        global money
        # print(lock.acquire())
        # if lock.acquire():
        #     for i in range(1000000):
        #         money += 1
        # lock.release()
        # 独占线程锁
        with lock:  # 阻塞直到拿到线程锁
    
            # -----下面的代码只有拿到lock对象才能执行-----
            for i in range(1000000):
                money += 1
            # 释放线程锁,以使其它线程能够拿到并执行逻辑
            # ----------------锁已被释放-----------------
    
        print(money
    
    # 5条线程同时访问money变量,导致结果不正确
    def conflictDemo():
        for i in range(5):
            t = threading.Thread(target=addMoney)
            t.start()
    
    # 通过线程同步(依次执行)解决线程冲突
    def handleConflictBySync():
        for i in range(5):
            t = threading.Thread(target=addMoney)
            t.start()
            t.join()  # 一直阻塞到t运行完毕
    
    # 通过依次独占线程锁解决线程冲突
    def handleConflictByLock():
        # 并发5条线程
        for i in range(5):
            t = threading.Thread(target=addMoneyWithLock)
            t.start()
    
    if __name__ == '__main__':
        # conflictDemo()
        # handleConflictBySync()
        handleConflictByLock()
    

    4、使用Semaphore调度线程:控制最大并发量

    '''
    使用Semaphore调度线程:控制最大并发量
    '''
    import threading
    import time
    # 允许最大并发量3
    sem = threading.Semaphore(3)
    
    def doSth(arg):
        with sem:
            tname = threading.current_thread().getName()
            print("%s正在执行【%s】" % (tname, arg))
            time.sleep(1)
            print("-----%s执行完毕!-----
    " % (tname))
            time.sleep(0.1)
    
    if __name__ == '__main__':
    
        # 开启10条线程
        for i in range(10):
            threading.Thread(target=doSth, args=("巡山",), name="小分队%d" % (i)).start()
        pass
    
  • 相关阅读:
    Windows Azure Platform Introduction (6) Windows Azure应用程序运行环境
    Windows Azure Platform Introduction (2) 云计算的分类和服务层次
    【转载】修改oracle的最大连接数 以及 object is too large to allocate on this o/s
    Windows Azure Platform Introduction (3) 云计算的特点
    Windows Azure Platform Introduction (8) Windows Azure 账户管理
    XML手册地址
    用dataset方式取值
    xml dataset的发布
    虚惊一场
    XML的一些特点
  • 原文地址:https://www.cnblogs.com/why957/p/9257848.html
Copyright © 2020-2023  润新知