python之多线程

多线程GIL全局解释器锁,保证同一时刻只有一个线程可以执行代码。GIL是一个历史遗留问题,主要出现在C语言解释器上,导致python的多线程是伪多线程。虽然Python的多线程是伪多线程,计算密集型任务单线程比多线程快,IO密集型任务多线程比单线程慢。所以在python中如果想要尽可能高的利用多核CPU的性能,还是要使用多进程。threading模块此模块的是Python自带的用于实现多线程的模块。创建线程的两种方法:#-------------------------------
摘要由CSDN通过智能技术生成

多线程

GIL

  • 全局解释器锁,保证同一时刻只有一个线程可以执行代码
  • GIL是一个历史遗留问题,主要出现在C语言解释器上,导致python的多线程是伪多线程。
  • 虽然Python的多线程是伪多线程,计算密集型任务单线程比多线程快IO密集型任务多线程比单线程慢
  • 所以在python中如果想要尽可能高的利用多核CPU的性能,还是要使用多进程。

threading模块

  • 此模块的是Python自带的用于实现多线程的模块。
  • 创建线程的两种方法:
#-------------------------------------------------------------
#						函数式创建
#-------------------------------------------------------------
import threading 

# 定义一个函数,作为线程任务
def game():
	print('--------game--------')

def main():
    # 创建一个线程
	# target参数传入函数名称
	t1 = threading.Thread(target=game)
    # 启动一个线程
    t1.start()
    
if __name__ = '__main__':
    main()
    

#-------------------------------------------------------------
#						创建线程类
#-------------------------------------------------------------
class MyThead(threading.Thread):
    #run函数是必须有的,线程启动后首先执行run函数里的代码(里面不一定是函数)
    def run(self):
        function()
    
    def function(self):
        pass
    
if __name__ = "__main__":
    # 创建线程
	for i in range(10):
        t = MyThread()
        t.run()

setDeamon()、join()

setDeamon()

  • 当创建多个线程后,默认的是主线程执行完后,等待子线程执行完毕,然后退出程序。
  • 但设置了守护线程后,一切以主线程为主,一旦主线程执行完毕,就会立即结束所有的子线程,不论子线程是否结束,然后退出程序。
t1 = threading.Thread(target=function)
t1.setDeamon(True)

join()

  • 如果不设置join(),主线程在子线程启动后,会继续往下执行。

  • 设置了join()后,主线程会等待子线程运行结束后再继续往下运行。

  • 没有设置join()

import threading
import time

def funcA():
    print("funcA启动")
    time.sleep(2)
    print("funcA结束")
    return

def funcB():
    print("funcB启动")
    time.sleep(2)
    print("funcB结束")
    return


if __name__ == '__main__':
    start = time.time()
    t1 = threading.Thread(target=funcA)
    t2 = threading.Thread(target=funcB)
    t1.start()
    t2.start()
    total = time.time() - start
    print(f"耗时{total}")
    
"""
funcA启动
funcB启动
耗时0.0
funcB结束funcA结束
"""
  • 设置了join()
import threading
import time

def funcA():
    print("funcA启动")
    time.sleep(2)
    print("funcA结束")
    return

def funcB():
    print("funcB启动")
    time.sleep(3)
    print("funcB结束")
    return


if __name__ == '__main__':
    start = time.time()
    t1 = threading.Thread(target=funcA)
    t2 = threading.Thread(target=funcB)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    total = time.time() - start
    print(f"耗时{total}")
    
"""
funcA启动
funcB启动
funcA结束
funcB结束
耗时3.0100889205932617
"""

线程间通信

  • 全局变量。线程间是共享全局变量的。所以可以用全局变量来进行线程间通信。太low了,我就不说了。

  • 队列。队列Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递

  • 使用方法:

from queue import Queue

# 实例化该数据结构,设置数据量上限1000条,如果不设置则没有上限
q = Queue(maxsize=1000)
# 向队列中推入数据
q.put()
# 向队列中取数据
q.get()
# 获取当前队列数据条数
q.qsize()
# 判断当前队列是否已满
q.full()
# 判断队列是否为空
q.empty()
# 等到队列为空再执行别的操作
q.join()

互斥锁

  • 当同一时刻有多个线程对共有资源(全局变量)进行操作时,可能会出现资源竞争,所以我们需要互斥锁,确保同一时刻只能有一个线程对共有资源进行操作,避免出现资源竞争,导致结果错误。
  • 资源竞争:
# 示例:
import threading
import time

num = 0
def one():
    global num
    # 上锁
    lock1.acquire()
    for i in range(1000000):
        num += 1
    # 解锁
    lock1.release()

def two():
    global num
    for j in range(1000000):
        lock1.acquire()
        num += 1
        lock1.release()

if __name__ == '__main__':
    t1 = threading.Thread(target=one)
    t2 = threading.Thread(target=two)
    t1.start()
    t2.start()
    # 休眠两秒,确保结果是最终结果
    time.sleep(2)
    print(num)
    
# 当同时有两个线程对全局变量num进行累加操作时,因为没有互斥锁,所以会导致
# 资源竞争导致最后的结果不是我们想要的200000
  • 上锁后,同一时刻只能有一个线程对num进行加一操作:
# 示例:
import threading
import time

num = 0
def one():
    global num
    # 上锁
    lock1.acquire()
    for i in range(1000000):
        num += 1
    # 解锁
    lock1.release()

def two():
    global num
    for j in range(1000000):
        lock1.acquire()
        num += 1
        lock1.release()

if __name__ == '__main__':
    # 创建互斥锁
    lock1 = threading.Lock()

    t1 = threading.Thread(target=one)
    t2 = threading.Thread(target=two)

    t1.start()
    t2.start()
    # 休眠两秒,确保结果正确
    time.sleep(2)
    print(num)
    
# num 2000000

死锁

  • 出现死锁的两种情况:
    • 相互等待
    • 自我等待
  • 以下是相互等待的示例:
import threading
import time


def funA(lockA, lockB):
    lockA.acquire()
    print("funA get lockA")

    # 让funA休眠两秒,确保funB拿到B锁,人为使之形成死锁
    time.sleep(2)
    lockB.acquire()
    print("funA get lockB")
    lockB.release()

    lockA.release()

def funB(lockA, lockB):
    lockB.acquire()
    print("funB get lockA")

    lockA.acquire()
    print("funA get lockB")
    lockA.release()

    lockB.release()

if __name__ == '__main__':
    lockA = threading.Lock()
    lockB = threading.Lock()

    t1 = threading.Thread(target=funA)
    t2 = threading.Thread(target=funB)

    t1.start()
    t2.start()

递归锁(可重入锁)

  • 递归锁允许一个线程多次获取同一把锁。
  • 但是线程之间如果某线程获取了锁,其他线程还是要等待。
import threading

num = 0;

def funcA():
    for i in range(1,10000):
        # 采用递归锁就不会产生死锁
		lock.acquire()
        lock.acquire()
    	num += 1
        lock.release()
        lock.release()
    
if __name__ = "__main__":
    lock = threading.Rlock()
    t1 = threading.Tread(target=funcA)
    

条件变量condition

  • 条件变量用于复杂的线程间同步

  • 条件变量其实就是多个线程间在底层公用一把线程锁,可以是普通锁,也可以是递归锁。默认是递归锁。

  • 示例,简单生产者消费者模型:

from threading import Condition, Thread
import random, time


class Producer(Thread):
    def __init__(self, nums, condition):
        super().__init__()
        self.nums = nums
        self.cond = condition

    def run(self):
        while True:
            self.cond.acquire()
            num = random.randint(1, 101)
            print("产生一个随机数{}".format(num))
            time.sleep(0.5)
            self.nums.append(num)
            # 通知消费者
            self.cond.notify()
            # 挂起当前线程,并等待消费者通知
            self.cond.wait()


class Customer(Thread):
    def __init__(self, nums, condition):
        super().__init__()
        self.nums = nums
        self.cond = condition

    def run(self):
        while True:
            self.cond.acquire()
            num = self.nums.pop()
            print("消费者消费一个随机数{}".format(num))
            time.sleep(0.5)
            # 通知生产者
            self.cond.notify()
            # 挂起当前线程,并等待生产者通知
            self.cond.wait()

if __name__ == '__main__':
    condition = Condition()
    # 用列表作为线程间的共享资源,其实不严谨,在这里只是简单演示
    nums = []
    p = Producer(nums, condition)
    c = Customer(nums, condition)
    p.start()
    c.start()
    
"""
产生一个随机数1
消费者消费一个随机数1
产生一个随机数6
消费者消费一个随机数6
产生一个随机数7
消费者消费一个随机数7
产生一个随机数19
消费者消费一个随机数19
产生一个随机数12
消费者消费一个随机数12
.....
"""
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值