孤荷凌寒自学python第三十八天初识python的线程控制

时间:2021-10-19 12:26:26
 孤荷凌寒自学python第三十八天初识python的线程控制

(完整学习过程屏幕记录视频地址在文末,手写笔记在文末)

一、线程

在操作系统中存在着很多的可执行的应用程序,每个应用程序启动后,就可以看着是一个进程,当打开WINDOWS任务管理器时,在任务管理器的进程选项卡中列出的就是一个一个的进程,基本上每个应用程序都对应着至少一个进程。

在同一进程中,也许同时在做着不止一件事情,比如在向程序界面上显示信息和接受信息的同时,程序也在和远端服务器通信读取数据,则这儿至少有两个线程运行在同一个进程中。

我的简单理解是,在同一个操作系统的进程中,可以同时执行多个线程的任务。

二、threading模块

threading模块是Python 中主要的线程控制模块。

使用threading模块前必须先声明引用:

import threading

三、threading模块的常见子类与属性和方法

1

threading.activeCount()

执行此方法返回当前进程下所有活动的线程总数。

2

threading.enumerate()

此方法直接返回一个 迭代器,迭代器中包含了,所有当前进程下的所有线程的信息。

在实际测试中,发现在循环打印时,总是不打印出第0个线程的信息,没有核准原因。

见后面的测试代码与结果。

3

threading.Thread

这是threading模块下的子模块(类),特别注意Thread模块的首字母是大写的。这是最容易疏忽之处。

四、获取或定义得到一个threading.Thread线程对象

可以通过以下方式得到threading.Thread线程对象

1.第一种:

thread对象=threading.Thread(target=要让新线程执行的函数名 , args=这个函数需要的参数组成的元组)

测试代码:

def f1(n):

strtime=str(datetime.now())

print('线程' + n + '正在运行中....线程启动于:' +strtime)

t=threading.Thread(target=f1,args=('t1',))

上面代码中,我创建了一个线程对象 t ,这个线程如果启动,将执行函数f1定义好的内部代码块。

Thread类的建构代码(初始化函数)中,要求传递的两个实参,只能按【关键字参数】形式传递进去。

2.第二种

thread对象=threading.Thread(target=要调用的作为函数用的类名(此类的初始化方法需要的参数列表))

测试代码:

class myt(object):

def __init__(self,n):

self.n=n

def __call__(self):

strtime=str(datetime.now())

print('线程' + self.n + '正在运行中....线程启动于:' +strtime)

t=threading.Thread(target=myt('t1'))

这种方式中,线程t启动后,将执行的是一个可以当着函数来调用的类myt.

myt是一个可以被 当着函数来使用的类,因为在类的内部代码块中定义有:__call__()私有方法。

与第一种方式不同的地方在于,这次只向Thread初始化方法传递了一个关键字实参target。

3.第三种方式

直接写一个新的继承自threading.Thread类的子类,然后,用新的类来初始化得到一个thread对象。

测试代码:

class myt(threading.Thread):

def __init__(self,n):

threading.Thread.__init__(self)

self.n=n

def run(self):

strtime=str(datetime.now())

print('线程' + self.n + '正在运行中....线程启动于:' +strtime)

t=myt('t1')

首先建构了一个继自threading.Thread的类 myt,这个类的特点有:

(1)在子类的__init__()方法中,必须 先调用父类的__init__()方法,在我的上机测试过程中(见过程屏幕录像,这儿反复出错,结果发现就是没有先调用父类的__init()__方法,引发的错误。

而原因在测试当时没有思考出来,所以大家在我的测试屏幕录像中可以看到没有想明白,现在思考下,发现可能是因为在threading.Thread类本身的__init__()中要执行非常重要的初始化操作,才能保证thread本类实例化后的对象功能可用,我找到了threading.Thread类本身的__init__()方法的代码如下:

def __init__(self, group=None, target=None, name=None,

args=(), kwargs=None, *, daemon=None):

"""Thisconstructor should always be called with keyword arguments. Arguments are:

*group* should be None; reserved forfuture extension when a ThreadGroup

class is implemented.

*target* is the callable object to beinvoked by the run()

method. Defaults to None, meaningnothing is called.

*name* is the thread name. By default,a unique name is constructed of

the form "Thread-N" where Nis a small decimal number.

*args* is the argument tuple for thetarget invocation. Defaults to ().

*kwargs* is a dictionary of keywordarguments for the target

invocation. Defaults to {}.

If a subclass overrides theconstructor, it must make sure to invoke

the base class constructor (Thread.__init__())before doing anything

else to the thread.

"""

assert group is None, "groupargument must be None for now"

if kwargs is None:

kwargs = {}

self._target= target

self._name =str(name or _newname())

self._args =args

self._kwargs= kwargs

if daemon is not None:

self._daemonic= daemon

else:

self._daemonic= current_thread().daemon

self._ident= None

self._tstate_lock= None

self._started= Event()

self._is_stopped= False

self._initialized= True

# sys.stderr is notstored in the class like

# sys.exc_infosince it can be changed between instances

self._stderr= _sys.stderr

# For debugging and_after_fork()

_dangling.add(self)

以上代码可以证明我的猜测是正确的,因此在测试屏幕录像中我没有想通的问题现在基本上有答案了,当然正确与否希望大家批评指正。

(2)在定义继承自threading.Thread类的子类中,__call__()方法 需要被 改名为 run()方法。

五、threading.Thread对象的主要属性与方法有:

1 name属性

此属性可读写,用以设置和获取线程对象的名称。

2 getName()方法

用于获取线程对象的名称。

3 setName()方法

用于设置线程对象的名称。

4 ident

此属性是只读的,获取线程对象的标识码,标识码是数字。

5 is_alive()  isAlive()

这两个方法用于判断线程对象是否正在运行(存活),返回布尔对象。

6 join()

这是线程对象非常 重要 的方法,此方法有一个可选 形参 timeout

执行线程的此方法后,会将调用 此线程的上级线程 阻塞,然后等待此线程执行完成,如果为join()方法设置了timeout时间,那么从此线程开始执行起计时,达到timeout指定的时间限额时,将自动解除对上级线程的阻塞。

在今天 的测试过程中,没有着重测试此方法的具体功用,理解可能不完全准确。

7 start()

线程执行此方法后,线程就开始执行指定任务,同时线程的isAlive标识将显示为True

此外,我没有发现线程有stop()方法,目前也没有研究到中止指定线程执行的其它方法。

测试代码一 :

importthreading

from datetimeimport datetime

intcount=3

class myt(object):

def __init__(self,n):

self.n=n

def __call__(self):

strtime=str(datetime.now())

print('线程' + self.n + '正在运行中....线程启动于:' +strtime)

def main():

threads=[]

x=range(intcount)

for n in x:

t=threading.Thread(target=myt('t' + str(n)))

#if n==1:

#    t.setDaemon(True)

t.name='t' + str(n)

threads.append(t)

for n in x:

threads[n].start()

#print(threading.activeCount())

for item inthreading.enumerate():

print(item)

print('--------')

for item inthreads:

print(item)

print(threads[0].isAlive())

for n in x:

threads[n].join()

if __name__=='__main__':

main()

执行结果:

线程t0正在运行中....线程启动于:2018-08-1617:31:08.939618

线程t1正在运行中....线程启动于:2018-08-1617:31:08.939618

线程t2正在运行中....线程启动于:2018-08-1617:31:08.940647

<_MainThread(MainThread, started 4436)>

<Thread(t1, stopped 2444)>

<Thread(t2, stopped 8676)>

--------

<Thread(t0, stopped 3188)>

<Thread(t1, stopped 2444)>

<Thread(t2, stopped 8676)>

False

测试代码二:

importthreading

fromdatetime import datetime

intcount=3

class myt(threading.Thread):

def __init__(self,n):

threading.Thread.__init__(self)

self.n=n

def run(self):

strtime=str(datetime.now())

print('线程' + self.n + '正在运行中....线程启动于:' +strtime)

def main():

threads=[]

x=range(intcount)

for n in x:

t=myt('t' + str(n))

#if n==1:

#    t.setDaemon(True)

t.name='t' + str(n)

threads.append(t)

for n in x:

threads[n].start()

#print(threading.activeCount())

for item inthreading.enumerate():

print(item)

print('--------')

for item inthreads:

print(item)

print(threads[0].isAlive())

for n in x:

threads[n].join()

if __name__=='__main__':

main()

执行结果:

线程t0正在运行中....线程启动于:2018-08-1617:32:36.893226

线程t1正在运行中....线程启动于:2018-08-1617:32:36.894223

线程t2正在运行中....线程启动于:2018-08-1617:32:36.895221

<_MainThread(MainThread, started 8884)>

<myt(t2, stopped 8940)>

--------

<myt(t0, stopped 7152)>

<myt(t1, stopped 8744)>

<myt(t2, stopped 8940)>

False

注意执行结果中,threading.enumerate()方法返回的迭代器在循环取出对象时,总没有完整打印,且有空行。这点没有思考出答案。搜索网络没有找到答案,还有待继续思考,并恳请高手予以指点。

——————————

今天整理的学习笔记完成,最后例行说明下我的自学思路:

根据过去多年我自学各种编程语言的经历,认为只有真正体验式,解决实际问题式的学习才会有真正的效果,即让学习实际发生。在2004年的时候我开始在一个乡村小学自学电脑 并学习vb6编程语言,没有学习同伴,也没有高师在上,甚至电脑都是孤岛(乡村那时还没有网络),有的只是一本旧书,在痛苦的自学摸索中,我找到适应自己零基础的学习方法:首先是每读书的一小节就作相应的手写笔记,第二步就是上机测试每一个笔记内容是否实现,其中会发现书中讲的其实有出入或错误,第三步就是在上机测试之后,将笔记改为电子版,形成最终的修订好的正确无误的学习笔记。

通过反复尝试错误,在那个没有分享与交流的黑暗时期我摸黑学会了VB6,尔后接触了其它语言,也曾听过付费视频课程,结果发现也许自己学历果然太低,就算是零基础的入门课程,其实也难以跟上进度,讲师的教学多数出现对初学者的实际情况并不了解的情况,况且学习者的个体也存在差异呢?当然更可怕的是收费课程的价格往往是自己难以承受的。

于是我的所有编程学习都改为了自学,继续自己的三步学习笔记法的学习之路。

当然自学的最大问题是会走那么多的弯路,没有导师直接输入式的教学来得直接,好在网络给我们带来无限搜索的机会,大家在网络上的学习日志带给我们共享交流的机会,而QQ群等交流平台、网络社区的成立,我们可以一起自学,互相批评交流,也可以获得更有效,更自主的自学成果。

于是我以人生已过半的年龄,决定继续我的编程自学之路,开始学习python,只希望与大家共同交流,一个人的独行是可怕的,只有一群人的共同前进才是有希望的。

诚挚期待您的交流分享批评指点!欢迎联系我加入从零开始的自学联盟。

这个时代互联网成为了一种基础设施的存在,于是本来在孤独学习之路上的我们变得不再孤独,因为网络就是一个新的客厅,我们时刻都可以进行沙龙活动。

非常乐意能与大家一起交流自己自学心得和发现,更希望大家能够对我学习过程中的错误给予指点——是的,这样我就能有许多免费的高师了——这也是分享时代,社区时代带来的好福利,我相信大家会的,是吧!

根据完全共享的精神,开源互助的理念,我的个人自学录制过程是全部按4K高清视频录制的,从手写笔记到验证手写笔记的上机操作过程全程录制,但因为4K高清文件太大均超过5G以上,所以无法上传至网络,如有需要可联系我QQ578652607对传,乐意分享。上传分享到百度网盘的只是压缩后的720P的视频。

我的学习过程录像百度盘地址分享如下:(清晰度:1280x720)

链接:https://pan.baidu.com/s/1n00vs2zpHiMCCf5eqykGrg

提取码:bgu2

Bilibili:

https://www.bilibili.com/video/av38088874/

喜马拉雅语音笔记:

https://www.ximalaya.com/keji/19103006/144900713

孤荷凌寒自学python第三十八天初识python的线程控制

孤荷凌寒自学python第三十八天初识python的线程控制

孤荷凌寒自学python第三十八天初识python的线程控制