python中线程的详细解析(join()和守护线程)

进程和线程
一、进程
进程是程序的分配资源的最小单元;一个程序可以有多个进程,但只有一个主进程;进程由程序、数据集、控制器三部分组成。
二、线程
线程是程序最小的执行单元;一个进程可以有多个线程,但是只有一个主线程;线程切换分为两种:一种是I/O切换,一种是时间切换(I/O切换:一旦运行I/O任务时便进行线程切换,CPU开始执行其他线程;时间切换:一旦到了一定时间,线程也进行切换,CPU开始执行其他线程)。
三、总结
程序的工作方式:
1.单进程单线程;2.单进程多线程;3.多进程多线程;
考虑到实现的复杂性,一般最多只会采用单进程多线程的工作方式;
四、为什么要使用多线程
我们在实际生活中,希望既能一边浏览网页,一边听歌,一边打游戏。这时,如果只开一个进程,为了满足需求,CPU只能快速切换进程,但是在切换进程时会造成大量资源浪费。所以,如果是多核CPU,可以在同时运行多个进程而不用进行进程之间的切换。
然而,在实际中,比如:你在玩游戏的时候,电脑需要一边显示游戏的动态,一边你还得和同伴进行语音或语言进行沟通。这时,如果是单线程的工作方式,将会造成在操作游戏的时候就无法给同伴沟通,在和同伴沟通的时候就无法操作游戏。为了解决该问题,我们可以开启多线程来共享游戏资源,同时进行游戏操作和沟通。

五 python 多线程

1.线程的意义

不用线程时:

import threading
import time
def music(name):
    print('%s begin listen music%s'%(name,time.ctime()))
    time.sleep(3)
    print('%s stop listen music%s' % (name, time.ctime()))
def game(name):
    print('%s begin play game%s'%(name,time.ctime()))
    time.sleep(3)
    print('%s stop play game%s' % (name,time.ctime()))
if __name__ == '__main__':
    music('zhang')
    game('zhang')
    print('Ending now %s'%time.ctime())

执行结果:

zhang begin listen musicSat Aug 31 17:31:54 2019
zhang stop listen musicSat Aug 31 17:31:57 2019
zhang begin play gameSat Aug 31 17:31:57 2019
zhang stop play gameSat Aug 31 17:32:00 2019
Ending now Sat Aug 31 17:32:00 2019

用线程时:

import threading
import time
def music(name):
    print('%s begin listen music%s'%(name,time.ctime()))
    time.sleep(3)
    print('%s stop listen music%s' % (name, time.ctime()))
def game(name):
    print('%s begin play game%s'%(name,time.ctime()))
    time.sleep(3)
    print('%s stop play game%s' % (name,time.ctime()))
if __name__ == '__main__':
    threadl = []
    t1 = threading.Thread(target=music,args=('zhang',))
    t2 = threading.Thread(target=game,args=('zhang',))
    t1.start()
    t2.start()
    print('Ending now %s'%time.ctime())
    

执行结果:

zhang begin listen musicSat Aug 31 18:02:40 2019
zhang begin play gameSat Aug 31 18:02:40 2019
Ending now Sat Aug 31 18:02:40 2019
zhang stop listen musicSat Aug 31 18:02:43 2019
zhang stop play gameSat Aug 31 18:02:43 2019

不使用线程时一共的运行时间是6秒。并且是只能单一按照顺序依次去执行。而使用多线时,运行时间是3秒,并且是并行执行。
该情况下的多线程运行方式是,先创建线程1,再创建线程2,然后去启动线程1和线程2,并和主线程同时运行。此种情况下,若子线程先于主线程运行完毕,则子线程先关闭后主线程运行完毕关闭;若主线程先于子线程结束,则主线程要等待所有的子线程运行完毕后再关闭。

2.join() 用法

import threading
import time
def music(name):
    print('%s begin listen music%s'%(name,time.ctime()))
    time.sleep(5)
    print('%s stop listen music%s' % (name, time.ctime()))
def game(name):
    print('%s begin play game%s'%(name,time.ctime()))
    time.sleep(3)
    print('%s stop play game%s' % (name,time.ctime()))
if __name__ == '__main__':
    threadl = []    #线程列表,用例存放线程
    #产生线程的实例
    t1 = threading.Thread(target=music,args=('zhang',)) #target是要执行的函数名(不是函数),args是函数对应的参数,以元组的形式;
    t2 = threading.Thread(target=game,args=('zhang',))
    threadl.append(t1)
    threadl.append(t2)
    #循环列表,依次执行各个子线程
    for x in threadl:
        x.start()
    #将最后一个子线程阻塞主线程,只有当该子线程完成后主线程才能往下执行
    x.join()
    print('Ending now %s'%time.ctime())

执行结果:

zhang begin listen musicSat Aug 31 18:10:46 2019
zhang begin play gameSat Aug 31 18:10:46 2019
zhang stop play gameSat Aug 31 18:10:49 2019
Ending now Sat Aug 31 18:10:49 2019
zhang stop listen musicSat Aug 31 18:10:51 2019

在实际中,需要子线程在插入数据,主线程需要等待数据插入结束后才能进行查询验证操作(测试验证数据)

使用join()作用是发生线程阻塞,即join后的线程结束后 主线程才结束

3.线程守护(setDaemon()函数)

前面不管是不是用到了join()函数,主线程最后总是要得所有的子线程执行完成后且自己执行完才能关闭(以子线程为主来结束主线程)。下面,我们讲述一种以主线程为主的方法来结束主线程。

import threading
import time
def music(name):
    print('%s begin listen music%s'%(name,time.ctime()))
    time.sleep(2)
    print('%s stop listen music%s' % (name, time.ctime()))
def game(name):
    print('%s begin play game%s'%(name,time.ctime()))
    time.sleep(5)
    print('%s stop play game%s' % (name,time.ctime()))
if __name__ == '__main__':
    threadl = []    #线程列表,用例存放线程
    #产生线程的实例
    t1 = threading.Thread(target=music,args=('zhang',)) #target是要执行的函数名(不是函数),args是函数对应的参数,以元组的形式;
    t2 = threading.Thread(target=game,args=('zhang',))
    threadl.append(t1)
    threadl.append(t2)
    #循环列表,依次执行各个子线程
    t2.setDaemon(True) #t2线程守护
    for x in threadl:
        x.start()
    #将子线程t1阻塞主线程,只有当该子线程完成后主线程才能往下执行
    print('Ending now %s'%time.ctime())

执行结果:

zhang begin listen musicSat Aug 31 18:17:59 2019
zhang begin play gameSat Aug 31 18:17:59 2019
Ending now Sat Aug 31 18:17:59 2019
zhang stop listen musicSat Aug 31 18:18:01 2019

所谓’线程守护’,就是主线程不管该线程的执行情况,只要是其他子线程结束且主线程执行完毕,主线程都会关闭。也就是说:主线程不等待该守护线程的执行完再去关闭。
注意:setDaemon方法必须在start之前且要带一个必填的布尔型参数

六、自定义的方式来产生多线程

import threading
import time
class mythread1(threading.Thread):
    '自定义线程'
    def __init__(self,name):
        threading.Thread.__init__(self)
        self.name=name
    def run(self):
        '定义每个线程要运行的函数,此处为music函数'
        print('%s begin listen music, %s' % (self.name, time.ctime()))
        time.sleep(5)
        print('%s stop listen music, %s' % (self.name, time.ctime()))

class mythread2(threading.Thread):
    '自定义线程'
    def __init__(self,name):
        threading.Thread.__init__(self)
        self.name=name
    def run(self):
        '定义每个线程要运行的函数,此处为game函数'
        print('%s begin play game, %s' % (self.name, time.ctime()))
        time.sleep(2)
        print('%s stop play game, %s' % (self.name, time.ctime()))
if __name__ == '__main__':
    threadl = []
    t1 = mythread1('zhang')
    t2 = mythread2('zhang')
    threadl.append(t1)
    threadl.append(t2)
    for x in threadl:
        x.start()
    print('Ending now %s' % time.ctime())

执行结果:

zhang begin listen music, Sat Aug 31 18:22:22 2019
zhang begin play game, Sat Aug 31 18:22:22 2019
Ending now Sat Aug 31 18:22:22 2019
zhang stop play game, Sat Aug 31 18:22:24 2019
zhang stop listen music, Sat Aug 31 18:22:27 2019

七、Threading的其他常用方法
getName() :获取线程名称
setName():设置线程名称
run():用以表示线程活动的方法(见七中自定义线程的run方法)
rtart():启动线程活动
is_alive():表示线程是否处于活动的状态,结果为布尔值;
threading.active_count():返回正在运行线程的数量
Threading.enumerate():返回正在运行线程的列表

import threading
import time
def music(name):
    print('%s begin listen music%s'%(name,time.ctime()))
    time.sleep(2)
    print('%s stop listen music%s' % (name, time.ctime()))
def game(name):
    print('%s begin play game%s'%(name,time.ctime()))
    time.sleep(5)
    print('%s stop play game%s' % (name,time.ctime()))
if __name__ == '__main__':
    threadl = []    #线程列表,用例存放线程
    #产生线程的实例
    t1 = threading.Thread(target=music,args=('zhang',)) #target是要执行的函数名(不是函数),args是函数对应的参数,以元组的形式;
    t2 = threading.Thread(target=game,args=('zhang',))
    threadl.append(t1)
    threadl.append(t2)
    #循环列表,依次执行各个子线程
    t2.setDaemon(True) #t2线程守护,setDaemon方法必须在start之前且要带一个必填的布尔型参数
    t1.setName('线程1')   #设置线程的名字
    for x in threadl:
        print('线程为:',x.getName())   #获取线程的名字
        print('线程t1是否活动:',t1.is_alive())    #判断线t1是否处于活动状态
        x.start()
    print('正在运行线程的数量为:',threading.active_count())   #获取正处于活动状态线程的数量
    print('正在运行线程的数量为:',threading.activeCount)       #获取正处于活动状态线程的数量
    print('正在运行线程的list为:',threading.enumerate())     #获取正处于活动状态线程的list
    print('正在运行线程的list为:',threading._enumerate())   #获取正处于活动状态线程的list
    #将子线程t1阻塞主线程,只有当该子线程完成后主线程才能往下执行
    print('正在运行的线程为:',threading.current_thread().getName()) #获取当前线程的名字
    print('Ending now %s'%time.ctime())

执行结果:

线程为: 线程1
线程t1是否活动: False
zhang begin listen musicSat Aug 31 18:30:13 2019
线程为: Thread-2
线程t1是否活动: True
zhang begin play gameSat Aug 31 18:30:13 2019
正在运行线程的数量为: 3
正在运行线程的数量为: <function active_count at 0x0000025F16F1CEA0>
正在运行线程的list为: [<_MainThread(MainThread, started 8432)>, <Thread(线程1, started 10348)>, <Thread(Thread-2, started daemon 15640)>]
正在运行线程的list为: [<_MainThread(MainThread, started 8432)>, <Thread(线程1, started 10348)>, <Thread(Thread-2, started daemon 15640)>]
正在运行的线程为: MainThread
Ending now Sat Aug 31 18:30:13 2019
zhang stop listen musicSat Aug 31 18:30:15 2019

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值