Python中的进程与线程、多线程、线程锁

Python中的进程与线程、多线程、线程锁,第1张

 “ 本文通过实例对比方式,详细说明了多线程的建立、多线程函数join()的应用,守护进程setDaemon()弃用后解决的新方法,简单互斥锁的举例。”

PS:“ 文末有核心总结,可自行享用 。”

  • 博主每篇文章的注释都是干货!每个代码段都有详细注释,一定要认真看注释!!!
  • 重要的事情说三遍:一定要看注释!!!一定要看注释!!!一定要看注释!!!

 程序、进程线程基本概念
  • 程序:静态的代码,程序运行后至少创建一个进程

  • 进程:执行的代码(程序),进程占用内存资源,不同进程间内存和数据都是独立的,一个进程至少包含一个线程

  • 线程:CPU执行,一个进程中可以有多个线程,同一进程中的线程,共享内存、共享数据共享

    • 多核CPU多线程——并发执行——高效率

    • 共享数据共享(数据安全性)

    • 多线程:程序下”不同函数同时执行

  • 线程建立实例引读(仅阐述语法大致区别,具体内容下文讲解): 

        单线程:

# 单线程执行
def run(newname):
    print("我是进程%s..." % newname)

for i in range(3):
    run(str(i))

       多线程(使用复杂方法建立,下文介绍简单方法):

# 多线程执行
class My(threading.Thread):     # 继承Thread类
    def __init__(self, newname):
        super(My, self).__init__(name=newname)  # 改变父类构造函数中的name参数,super('子类名',self).__init__('改变的参数')

    def run(self):  # 定义多线程执行语句
        print("我是进程%s..." % self.name)

for i in range(3):
    My(str(i)).start()    # start()执行
    

多线程建立
  • 核心:
  • target传入执行名!!!run不是run()否则会串行执行
  • args传入元组变量(i,)
  • start()执行
# 多线程执行语法实例:

import threading
# 定义执行函数
def run(i):
    print('我是进程%d'%i)

for i in range(3):
    T = threading.Thread(target=run, args=(i,))     # 实例化,target为执行函数名,args为元组变量(i,),不能写(i),因为(i)是int变量
    T.start()   # 一定要用start()执行
  • 核心思想:
  • 三句语句的打印是同时进行的!!!
  • 三句语句的打印是同时进行的!!!

  


主、子线程的等待与停止  线程合并函数join()——子线程为1个
  • join()作用:等待指定进程结束后再执行后续进程
  • 核心
  • join()在start()之后
  • 没有使用join()前:
# 多线程执行顺序验证
# 预期结果:按照1.2.3.4顺序执行打印
# import threading
# import time
# 子线程
def My():
    print('2.子线程开始')
    time.sleep(3)
    print('3.子线程结束')

# 主线程
print('1.主线程开始')

# 此实例仅同时执行一个子线程
T = threading.Thread(target=My)
T.start()

print('4.主线程结束')

# 实际运行结果,1.2.4.3
# 因为多线程中如果不加join(),主线程不会等待子线程执行完再执行,是并发执行的

  

  • 使用join()后: 
# join()函数的使用
import threading
import time
# 子线程
def My():
    print('2.子线程开始')
    time.sleep(3)
    print('3.子线程结束')

# 主线程
print('1.主线程开始')

# 此实例仅同时执行一个子线程
T = threading.Thread(target=My)    # 传入My不是My()
T.start()
T.join()    # start()后加上join()等待线程T结束

print('4.主线程结束')

# 执行结果为1.2.3.4

  

守护进程setDaemon()(已弃用) 守护进程daemon属性(新版)——子线程为多个
  • 主进程结束时,停止一切子进程
  • 未使用守护进程daemon时
# 未使用守护进程
import threading
import time

# threading.Thread.daemon = True
# 子线程
def My():
    print('2.子线程开始', threading.current_thread().name)
    # threading.current_thread().name可以获取线程名字
    time.sleep(2)    # 子线程睡眠2秒
    print('3.子线程结束------', threading.current_thread().name)
# 主线程
print('1.主线程开始')
# 此实例同时执行2个子线程
for i in range(2):
    T = threading.Thread(target=My)
    T.start()
print('4.主线程结束','\n')
  • 主进程结束后两个子进程仍然进行 

  

  • 使用守护进程daemon后 
  • 核心语句:在引入多线程之前定义进程属性为true
  • threading.Thread.daemon = True
import threading
import time

threading.Thread.daemon = True    # 引入守护进程
# 子线程
def My():
    print('2.子线程开始', threading.current_thread().name)
    # threading.current_thread().name可以获取线程名字
    time.sleep(2)    # 子线程睡眠2秒
    print('3.子线程结束------', threading.current_thread().name)
# 主线程
print('1.主线程开始')
# 此实例同时执行2个子线程
for i in range(2):
    T = threading.Thread(target=My)
    T.start()
print('4.主线程结束','\n')

 

*进阶探索(可跳过)
  • 设置守护进程
  • 将target从My改为My()
import threading
import time

threading.Thread.daemon = True

# 子线程函数
def My():
    print('2.子线程开始', threading.current_thread().name)
    time.sleep(2)   
    print('3.子线程结束------', threading.current_thread().name)

# 主线程
print('1.主线程开始')

for i in range(2):
    T = threading.Thread(target=My())    传入类My()
    T.start()

print('4.主线程结束','\n')
  • 结果分析:
  • 守护进程失去作用,且各个线程为串行执行 

  


 多线程共享变量实例
# 大数据量才能体现多线程共享变量
# 实例:
# 每个线程将全局变量k加200万次
# 总共三个线程
# 由于是三个线程同时执行+共享变量k
# 结果最后k一定是300万,但是每一个线程执行完的结果都是不一定的
import threading
k = 0   # 全局变量k

# 定义单个线程的函数
def My():
    global k
    for i in range(2000000):
        k = k+1
    print('线程%s执行完毕,k=%d'%(threading.current_thread().name,k))

# 并发3个线程
for j in range(3):
    T = threading.Thread(target=My)
    T.start()
    # T.join()  # 此处一定不能加join(),否则效果相当于顺序执行而不是并发!!!
  • 多次实验,输出结果最后一项一定为600万,但是是哪一个线程结束都不固定(并发)

    


线程锁——解决多线程共享变量问题 
  • 互斥锁:一个变量同一时间只可被一个线程使用
  • 可重入锁
  • 事件锁
  • 互斥锁实例:
  • 核心,共享变量上锁时,其他线程停止执行,等待解锁,参考顺序执行概念
  • 步骤:
  • threading.Lock()获取线程锁
  • acquire()上锁,realease()解锁/with LK上锁+自动解锁
import threading
LK = threading.Lock()
k = 0
def My(LK):
    global k
    LK.acquire()  # 上锁
    print('线程%s开始执行,k=%d' % (threading.current_thread().name, k))
    for i in range(2000000):
        k = k+1
    print('线程%s执行完毕,k=%d'%(threading.current_thread().name,k))

    LK.release()  # 解锁
    
    # 也可使用with方法:
    # global k
    # with LK:
    #     print('线程%s开始执行,k=%d' % (threading.current_thread().name, k))
    #     for i in range(2000000):
    #         k = k + 1
    #     print('线程%s执行完毕,k=%d' % (threading.current_thread().name, k))

for j in range(3):
    T = threading.Thread(target=My,args=(LK,))  # 将LK以元组形式传入
    T.start()

time.sleep(5)
print(time.thread_time())

 


应用模板
  • 涵盖文章提到所有内容,按需求自行删减
# 应用模板
import threading
import time

k = 0
L = threading.Lock()  # 拿锁
threading.Thread.daemon = True  # 设置守护进程


# 单线程函数
def My(L):
    global k
    # L.acquire()
    print('线程%s开始执行,k=%d' % (threading.current_thread().name, k))
    for i in range(2000000):
        k = k + 1
    print('线程%s执行完毕,k=%d' % (threading.current_thread().name, k))
    # L.release()

    # with L:
    #     print('线程%s开始执行,k=%d' % (threading.current_thread().name, k))
    #     for i in range(2000000):
    #         k = k + 1
    #     print('线程%s执行完毕,k=%d' % (threading.current_thread().name, k))


# 多线程执行,3个线程
for i in range(3):
    t = threading.Thread(target=My, args=(i,))  # 传类名属性+元组
    # t = threading.Thread(target=My, args=(L,))  # 使用线程锁将i改为L
    t.start()
    # t.join()
time.sleep(5)  # 休息5秒防止前文定义的守护进程生效
print('所有线程执行完毕的k是:', k)
总结
  • 进程:占用内存,不同进程间内存和数据独立
  • 线程:CPU执行,一个进程中可以有多个线程,同一进程中的线程共享内存、共享数据
  • super('subname', self).__init__('father形参'='sub形参')改变父类构造函数
  • threading.Thread(target='类名', args=(i,)) 多线程传参,i\L,类名+参数元组
  • T.start() 线程执行
  • join()函数:等待指定进程结束后再执行后续进程,join()在start()之后
  • threading.Thread.daemon = True  守护进程daemon属性,主线程停止后所有进程停止

  • 传入类():守护进程失去作用,且各个线程为独立主线程串行执行 

  • 互斥锁:一个变量同一时间只可被一个线程使用

  • threading.Lock()获取线程锁

  • acquire()上锁,realease()解锁/with LK上锁+自动解锁

  • 线程锁与同步区别:需要手动关闭,可设定条件开关锁


💗 “Loving somebody is like loving a mountain, you can enjoy that, cross that, but you can't move that. Some people try to bask the love, and some people choose to keep away from the mountain. And saying that: the only way to move the mountain is traverse that by yourself and even in love.The fact is that they ignore a story in the history of China.When you carry about love, sincerity and patience which means you master the amazing ability to change it step by step as the time goes by.” 

                                                                                     ——Created By 是羽十八ya

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/917040.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存