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之间快速切换 , 交替执行

python多线程详解编程语言

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执行完毕!-----/n" % (tname)) 
        time.sleep(0.1) 
 
if __name__ == '__main__': 
 
    # 开启10条线程 
    for i in range(10): 
        threading.Thread(target=doSth, args=("巡山",), name="小分队%d" % (i)).start() 
    pass

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/12191.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论