Python学习笔记-线程

一、并发与并行

并发:逻辑上具备同事处理多个任务的能力

并行:物理上在同一时刻执行多个并发任务

二、进程与线程

打开QQ,开了一个进程;在QQ这个进程里,传输文件开了一个线程,与一个朋友聊天开了一个线程,打开一个小程序开了一个线程。

所以运行某个软件,相当于开了一个进程,在这个软件运行的过程里,多个工作同时进行,完成了QQ的运行,每个工作都对应一个线程,所以一个进程管着多个线程,一个进程至少有一个线程

import threading    #加载线程模块
def func1(sth):   #定义线程需要实现的功能函数
    print(sth)
#创建线程,target 线程需要实现的功能 args 传递前面实现功能的参数,以元组形式传递多个参数
t1 = threading.Thread(target=func1, args=('看电影',))   #创建一个看电影的线程
t2 = threading.Thread(target=func1, args=('听音乐',))   #创建一个听音乐的线程
#启动线程
t1.start()
t2.start()
'''
看电影
听音乐
'''

多个线程执行没有固定顺序,看操作系统的调度

在CPython解释器中,GIL(全局解释器锁)规定同一时间只允许一个线程进入解释器,多线程只是逻辑上存在的,即只能做到并发,不能做到并行,这是CPython的遗留问题,但由于CPython应用十分广泛,所以这个问题也就一直存在了。

import threading,time
def func1(sth):
    for i in range(5):
        time.sleep(1)     #等待1s,print执行速度太快,cpu可能还没有切换线程,就会连续执行同一个线程
        print(sth)
#创建线程,target 线程需要实现的功能 args 传递前面实现功能的参数,以元组形式传递多个参数
t1 = threading.Thread(target=func1, args=('看电影',))   #创建一个看电影的线程
t2 = threading.Thread(target=func1, args=('听音乐',))   #创建一个听音乐的线程
#启动线程
t1.start()
t2.start()

for i in range(5):
        time.sleep(1)
        print('父线程')
'''
父线程
听音乐
看电影
看电影父线程      #这种情况是上一个print还未结束,下一个print开始执行,然后才打印换行符

听音乐
看电影父线程听音乐


听音乐
父线程看电影

看电影父线程听音乐



'''

join()

如果要想先执行子进程,最后再执行主进程(进程启动之后,会默认产生一个主线程),需要用到join()

import threading,time
def func1(sth):
    for i in range(5):
        time.sleep(1)    
        print(sth)
#创建线程
t1 = threading.Thread(target=func1, args=('看电影',))   
t2 = threading.Thread(target=func1, args=('听音乐',))  
#启动线程
t1.start()
t2.start()
#在子线程完成运行之前,这个子线程的父线程将一直被阻塞
t1.join()
t2.join()

for i in range(5):
        time.sleep(1)
        print('父线程')
'''
听音乐
看电影
看电影听音乐

听音乐
看电影
看电影听音乐

听音乐
看电影
父线程
父线程
父线程
父线程
父线程
'''

守护线程

如果想在主线程执行完成后,停止子线程的执行,需要用到守护线程setDaemon(True)

import threading,time
def func1(sth):
    for i in range(10):
        time.sleep(1)     
        print(sth)
#创建线程
t1 = threading.Thread(target=func1, args=('看电影',))   
t2 = threading.Thread(target=func1, args=('听音乐',))   
#声明守护线程,必须在start调用之前
t1.setDaemon(True)
t2.setDaemon(True)
#启动线程
t1.start()
t2.start()

for i in range(5):
        time.sleep(1)
        print('父线程')
print('父线程完成')
'''
看电影
听音乐
父线程
听音乐
看电影
父线程
父线程
看电影
听音乐
父线程听音乐
看电影

看电影父线程听音乐

父线程完成
'''

多线程带来的不安全的并发

由上面的例子知道,线程在执行时没有先后顺序,这就会导致一些由于执行顺序不同而出现的问题

例:小明银行卡余额有500块,工资入账10000块,购物花了300,如果这两件事同时发生,有可能会造成两种错误的情况,一是工资覆盖了购物,余额为10500,二是购物覆盖了工资,余额为200,这都是不正确的,要想避免这个问题,需要用到

原始锁(同步锁)

当前只有一个线程可以使用,等释放后才能被其他线程使用

必须 上锁和解锁 成对出现,如果上锁两次,不解锁,代码会阻塞(死锁);如果上锁一次,解锁两次,代码会报错,因为没有锁可解

import threading,time
balance = 500   #银行卡余额
lock = threading.Lock()  #声明一把同步锁
def update_account(num):
    global balance  #声明要对全局变量进行操作
    lock.acquire()   #上锁
    time.sleep(2)   # 防止代码太少,CPU执行速度太快,t1和t2变成串行
    new_balance = balance+num    #新的余额
    balance = new_balance    #将新余额赋值给全局变量的银行卡余额
    lock.release()    #解锁
t1 = threading.Thread(target=update_account, args=(10000,))   #创建一个线程,存入工资
t2 = threading.Thread(target=update_account, args=(-300,))    #创建一个线程,用于购物消费
#启动线程
t1.start()
t2.start()
#阻塞主线程
t1.join()
t2.join()
print('最终账户余额为:', balance)     #最终账户余额为: 10200

死锁

两个线程分别占有一部分资源,但又同时等待对方的资源

import threading, time

alock = threading.Lock()
block = threading.Lock()
def xiaoming():
    alock.acquire()
    print("小明请求资源A")
    time.sleep(1)
    block.acquire()
    print("小明请求资源B")
    time.sleep(1)
    alock.release()
    block.release()
def xiaohong():
    block.acquire()
    print("小红请求资源B")
    time.sleep(1)
    alock.acquire()
    print("小红请求资源A")
    time.sleep(1)
    alock.release()
    block.release()

t1 = threading.Thread(target = xiaoming)
t2 = threading.Thread(target = xiaohong)

t1.start()
t2.start()
'''
小明请求资源A
小红请求资源B
(一直卡在这边)
'''

递归锁(重入锁)

是为了支持同一线程中多次请求同一资源(不会被阻塞)的锁,当这个线程完全释放锁后,其他线程才可以使用这个锁

递归锁内部维护着一个计数器和一把锁,计数器记录了上锁的次数
每次上锁,计数器加一
每次解锁,计数器减一
计数器可以大于零也可以等于零,但是不能小于零

import threading, time

rlock = threading.RLock()   #重入锁
def xiaoming():
    rlock.acquire()
    print("小明请求资源A")
    time.sleep(1)
    rlock.acquire()
    print("小明请求资源B")
    time.sleep(1)
    rlock.release()
    rlock.release()

def xiaohong():
    rlock.acquire()
    print("小红请求资源B")
    time.sleep(1)
    rlock.acquire()
    print("小红请求资源A")
    time.sleep(1)
    rlock.release()
    rlock.release()

t1 = threading.Thread(target = xiaoming)
t2 = threading.Thread(target = xiaohong)

t1.start()
t2.start()
'''
小明请求资源A
小明请求资源B
小红请求资源B
小红请求资源A
'''
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值