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


     孤荷凌寒自学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

    图片

    图片

    图片

    欢迎大家添加我为好友: QQ: 578652607
  • 相关阅读:
    Vim基本功
    八个最常用的正则表达式
    程序员总结:帮助你早些明白一些道理
    HttpClient
    Red.Hat.Enterprise.Linux.6.2下安装vim 、OpenOffice、JDK、Eclipse
    输出打印某个对象所有属性及属性值
    Notepad++
    写博客?
    解决操作WordPress时提示输入FTP信息
    JS通过键盘点击事件实现div移动
  • 原文地址:https://www.cnblogs.com/lhghroom/p/10125181.html
Copyright © 2020-2023  润新知