NO.3 Python多线程&锁

NO.3 Python多线程&锁

零蚀


线程

线程CPU独立调度和分配的基本单位。线程和进程共享应用的所有的资源。

  • 线程编码
import threading


def say():
    print("shout")
    time.sleep(0.5)
if __name__ == "__main__":
    for i in range(20):
        thread = threading.Thread(target=say)
        thread.start()

⚠️ **exit()**只能退出进程,并不能关闭进程运行的子线程。

  • 线程API

    • 获取活线程对象列表
    def say():
        print("shout")
        time.sleep(10)
    
    def thread_api():
        while True:
            print(threading.enumerate())
            print(len(threading.enumerate()))
            time.sleep(1)
        
    if __name__ == "__main__":
    
        for i in range(3):
            thread = threading.Thread(target=say)
            thread.start()
    
        thread_api()
    
    • print
    [<_MainThread(MainThread, started 4714063296)>, <Thread(Thread-1, started 123145492938752)>, <Thread(Thread-2, started 123145498193920)>, <Thread(Thread-3, started 123145503449088)>]
    4
    
    • 获取活线程数量
    #threading.activeCount()
    threading.active_count()
    
    • 参数传递
    '''
     元祖
    '''
    def say(i):
        print("shout",i)
        time.sleep(10)
    thread = threading.Thread(target=say,args=(18,))
    thread.start()
    
    '''
      字典
    ''' 
    def say(i):
        print("shout", i)
        time.sleep(10)
    thread = threading.Thread(target=say, kwargs={"i": 11})
    thread.start()
    
    # 也可以字典+元祖
    

    当主线程结束需要结束子线程的所有内容。执行exit()时子线程也全部退出,需要将子线程转变为守护线程,此方法必须在start之前:

    thread = threading.Thread(target=say, kwargs={"i": 11})
    # 设置为守护线程
    thread.setDaemon(True)
    thread.start()
    
    • 自定义线程
    class MyThread(threading.Thread):
    
        # 传参
        def __init__(self, num):
            super().__init__()
            self.num = num
    
    
    if __name__ == "__main__":
        thread = MyThread()
        thread.start()
    

    run函数内部本来是运行target指定的函数,但是重写了此函数,target就不会再执行了。

    • 共享全局变量
    def work1():
        global num
        for i in range(0, 100):
            num += 1
            print("num:",num)
    
        print("work1")
    
    
    def work2():
        global num
    
        for i in range(0, 100):
            num -= 1
            print("num1:", num)
    
    
    if __name__=="__main__":
    
        thread1=threading.Thread(target=work1)
        threading.Thread(target=work2).start()
    
        thread1.start()
    
        # 阻塞当前线程(在主线程里),知道线程执行结束,才执行主线程
        thread1.join()
        print("main")
    

    **join()**阻塞当前线程(在主线程里),知道线程执行结束,才执行主线程。

    • 线程问题
    def work1():
        global num
        for i in range(0, 1000000):
            num += 1
    
        print("work1:", num)
    
    
    def work2():
        global num
    
        for i in range(0, 1000000):
            num += 1
    
        print("work2:", num)
    
    
    if __name__ == "__main__":
        thread1 = threading.Thread(target=work1)
        thread2 = threading.Thread(target=work2)
    
        thread1.start()
        thread2.start()
    
        thread1.join()
        thread2.join()
        print("main:", num)
        
    #print
    #work1: 1313994
    #work2: 1523812
    #main: 1523812
    

    按道理说会是200,但是这里加到最后并不是200。

    CPU执行单元原理:

    work1
    work2
    2.num+1=x+1
    1.获取num=x
    3.num=x+1
    2.num+1=x+1
    1.获取num=x
    3.num=x+1

    在大规模的全局变量运算中,会有很大的概率,导致两个运算交叉。


线程锁

  • 互斥锁

将线程完全隔离,每次只保证一个线程在正常运作,

if __name__ == "__main__":
    thread1 = threading.Thread(target=work1)
    thread2 = threading.Thread(target=work2)

    # 创建锁
    lock = threading.Lock()
    # 锁定
    lock.acquire()

    thread1.start()
    thread1.join()

    # 释放
    lock.release()

    thread2.start()
    thread2.join()

    print("main:", num)

这里互斥锁将**lock.acquire()ock.release()**之间的内容进行锁定,使得其中的内容在运行时不被影响,其他线程只能被阻塞在原地。但是互斥锁是比较消耗时间的。

  • 死锁

当锁没有释放前发生了bug程序不能继续进行,而程序的互斥锁一直没有释放,所以程序会一直卡在锁里。lock的简洁写法如下,但是有异常还要自己释放。

# 创建锁
lock = threading.Lock()

with lock:
        pass
# 自己释放锁

GIL锁

python里面的一个进程只有一个CPU为其服务,python只有进程才能为应用获取系统资源。无论多少CPUpython执行的时候一个CPU有且仅有一个被允许运行一个进程。每个进程都有自己独立的GIL且互不干扰。只能利用多进程进行并行操作。

  • 多进程
import multiprocessing


def func():
    pass


process = multiprocessing.Process(target=func)
process.start()

递归锁

情况如下:

import threading
import time


def A(i):
    with lockA:
        print("获取到了A:", i)
        time.sleep(3)
        with lockB:
            print("A想获取B的lock",i)


def B(i):
    with lockB:
        print("获取到了B:", i)
        time.sleep(2)
        with lockA:
            print("B想获取A的lock",i)


class Mythread(threading.Thread):
    def __init__(self, index):
        super().__init__()
        self.index = index

    def run(self):
        A(self.index)
        B(self.index)


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

    for i in range(0, 5):
        newThread = Mythread(i)
        newThread.start()

A锁里面想拿B锁,B锁里面想拿A锁,互相拿锁过程中A()想拿BLock且没释放ALock,B()想拿ALock且没释放BLock,就形成了互掐的僵局,在这种锁里面套着锁定情况下,需要用递归锁。

递归锁里面有一个account参数,同一个线程里有可以acqurie多次申请一次同类,account+1,释放时候account-1,这样避免了一次的aqurie导致堵塞。

import threading
import time


def A(i):
    with rlock:
        print("获取到了A:", i)
        time.sleep(3)
        with rlock:
            print("A想获取B的lock",i)


def B(i):
    with rlock:
        print("获取到了B:", i)
        time.sleep(2)
        with rlock:
            print("B想获取A的lock",i)


class Mythread(threading.Thread):
    def __init__(self, index):
        super().__init__()
        self.index = index

    def run(self):
        A(self.index)
        B(self.index)


if __name__ == '__main__':
    rlock = threading.RLock()

    for i in range(0, 5):
        newThread = Mythread(i)
        newThread.start()


事件EVENT

event = threading.Event()

event有以下的方法:

方法注释
clear将flag设置为False
set将flag设置为True(无参)
is_set判断是否设置了flag
wait会一直监听flag,如果没有检测到flag为True就一直处于阻塞的状态

Timer

方法和thread一致

from threading import Timer


def work(i):
    print(i)


Timer(3, function=work,args=(1,)).start()

线程池(Python3.x)

线程池中会有队列,如果有新的任务线程过来,会进行任务排队,这样防止过多的创建线程,导致CPU卡死,和切换线程的巨大开销。在有限的线程中不断切换,可以节约内存。

from concurrent.futures import ThreadPoolExecutor
def say(str):
    print("say",str)


if __name__ == '__main__':
    with ThreadPoolExecutor(3) as pool:

        for i in range(6):
            pool.submit(say,"666")
            # pool.map(say,[1,2,3,4,5,6])

🔗 前言
🔗 Python 高级列表
🔗 NO.1 Python网络通信
🔗 NO.2 Python Web 服务器
🔗 NO.4 Python多进程
🔗 NO.5 Python协程&生成器
🔗 NO.6 Python正则&爬虫初
🔗 NO.7 Python操作MySQL
🔗 NO.8 Python Spider
🔗 NO.9 Python 装饰器&other

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零蚀zero eclipse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值