下载APP
登录
关闭
讲堂
算法训练营
Python 进阶训练营
企业服务
极客商城
客户端下载
兑换中心
渠道合作
推荐作者
当前播放: 39 | 多线程编程的定义
00:00 / 00:00
标清
  • 标清
1.0x
  • 2.0x
  • 1.5x
  • 1.25x
  • 1.0x
  • 0.5x
网页全屏
全屏
00:00
付费课程,可试看

零基础学Python

共71讲 · 71课时·约540分钟
16024
免费
01 | Python语言的特点
免费
02 | Python的发展历史与版本
免费
03 | Python的安装
免费
04 | Python程序的书写规则
免费
05 | 基础数据类型
免费
06 | 变量的定义和常用操作
07 | 序列的概念
08 | 字符串的定义和使用
09 | 字符串的常用操作
10 | 元组的定义和常用操作
11 | 列表的定义和常用操作
12 | 条件语句
13 | for循环
14 | while循环
15 | for循环语句中的if嵌套
16 | while循环语句中的if嵌套
17 | 字典的定义和常用操作
18 | 列表推导式与字典推导式
19 | 文件的内建函数
20 | 文件的常用操作
21 | 异常的检测和处理
22 | 函数的定义和常用操作
23 | 函数的可变长参数
24 | 函数的变量作用域
25 | 函数的迭代器与生成器
26 | Lambda表达式
27 | Python内建函数
28 | 闭包的定义
29 | 闭包的使用
30 | 装饰器的定义
31 | 装饰器的使用
32 | 自定义上下文管理器
33 | 模块的定义
34 | PEP8编码规范
35 | 类与实例
36 | 如何增加类的属性和方法
37 | 类的继承
38 | 类的使用-自定义with语句
39 | 多线程编程的定义
40 | 经典的生产者和消费者问题
41 | Python标准库的定义
42 | 正则表达式库re
43 | 正则表达式的元字符
44 | 正则表达式分组功能实例
45 | 正则表达式库函数match与s...
46 | 正则表达式库替换函数sub...
47 | 日期与时间函数库
48 | 数学相关库
49 | 使用命令行对文件和文件夹...
50 | 文件与目录操作库
51 | 机器学习的一般流程与NumP...
52 | NumPy的数组与数据类型
53 | NumPy数组和标量的计算
54 | NumPy数组的索引和切片
55 | pandas安装与Series结构
56 | Series的基本操作
57 | Dataframe的基本操作
58 | 层次化索引
59 | Matplotlib的安装与绘图
60 | 机器学习分类的原理
61 | Tensorflow的安装
62 | 根据特征值分类的模型和代...
63 | 网页数据的采集与urllib库
64 | 网页常见的两种请求方式ge...
65 | HTTP头部信息的模拟
66 | requests库的基本使用
67 | 结合正则表达式爬取图片链...
68 | Beautiful Soup的安装和...
69 | 使用爬虫爬取新闻网站
70 | 使用爬虫爬取图片链接并下...
71 | 如何分析源代码并设计合理...
本节摘要

课程源码、课件及课后作业地址:

https://github.com/wilsonyin123/geekbangpython

精选留言(20)

  • 2018-07-20
    老师,我想不明白你重写这个run方法之后为什么这个线程还能够执行,之前是start函数调用原来这个run方法,你现在将它重写啦,重写不是覆盖前面父类的方法吗,您重写的run方法只是打印而已,怎么会执行呢?

    作者回复:
    Thread是threading模块的一个用于创建线程的类,创建线程有两个方法,一个是通过继承Thread类,重写run方法;我在视频中演示的Mythread类就是这种方法了,要注意覆盖的是父类继承过来的run方法,start还是继承过来不变的。这里要注意的是start()和run() 是Thread的两种不同方法,官方定义在这里:https://docs.python.org/3.7/library/threading.html
    那么把他们的解释翻译成中文就是 start() 开始线程工作,把run()方法放到一个另外的单独的线程里执行 ;run() 线程工作方法,在“当成”线程执行函数里面的代码。
    所以他们的作用一个是开启新线程,一个是按照线程的方式执行程序。我们可以写个小程序来验证一下
    import threading
    from threading import current_thread

    class Mythread(threading.Thread):
        def run(self):
            print(current_thread().getName())


    t1 = Mythread()
    t1.run()
    t1.start()

    这段程序的执行结果是:
    MainThread
    Thread-1
    也就是说单独运行run()方法,会把主进程当做一个线程来看,执行的代码空间在MainThread主线程中,执行了start()方法,python会新创建一个线程,叫Thread-1,然后再去调用run()来运行,这就是他们两个的区别了。


    5
  • 2018-11-14
    老师,这里用join方法是等线程执行完成后再回到主线程继续执行后续的代码。但当有多个线程时,如果每个线程都调用join方法,这样线程2就会等线程1执行完毕,再继续执行。会不会就失去了并发的意义?
    代码如下:
    import time
    import threading
    from threading import currentThread

    class MyThread(threading.Thread):
        def run(self):
            print(currentThread().getName(), 'start')
            time.sleep(5)
            print(currentThread().getName(), 'stop')

    for i in range(1, 6, 1):
        thread = MyThread()
        thread.start()
        thread.join()

    print ('Main Thread stop')

    输出结果:
    Thread-1 start
    Thread-1 stop
    Thread-2 start
    Thread-2 stop
    Thread-3 start
    Thread-3 stop
    Thread-4 start
    Thread-4 stop
    Thread-5 start
    Thread-5 stop
    Main Thread stop
    展开

    作者回复: 做这样一个实验,thread.join()这条语句放在for循环中,放在for循环外(即这条语句前面没有空格),注释这条语句,观察一下输出有什么变化?你能解释三种变化的原因吗?

    1
    3
  • 2018-10-16
    歪打正着,在 sleep() 前后各进行打印操作,多线程的运行逻辑就很明白了。

    import time, threading

    def basedThread(temp_a, temp_b):
        print("第一个参数是:%s,第二个参数是:%s" %(temp_a, temp_b))
        time.sleep(1) # 这里执行完之后,暂停1s
        print("暂停了1s")
        
    for i in range(1, 6, 1):
        t1 = threading.Thread(target=basedThread, args=(i, i**2)) ## 只是创建一个多线程程序
        t1.start() # 运行 t1 这个多线程程序
    展开
    1
    3
  • 2019-07-11
    老师,为什么MainTHread在5 6之前
    程序
    import threading
    import time
    from threading import current_thread

    def myThread(arg1, arg2):
        print(current_thread().getName(),'start')
        print('%s %s'%(arg1, arg2))
        time.sleep(1)
        print(current_thread().getName(),'stop')

    for i in range(1,6,1):
        #t1 = myThread(i, i+1)
        t1 = threading.Thread(target=myThread,args=(i, i+1))
        t1.start()

    print(current_thread().getName(),'end')

    结果
    Thread-1 start
    1 2
    Thread-2 start
    2 3
    Thread-3 start
    3 4
    Thread-4 start
    4 5
    Thread-5 start
    MainThread end
    5 6
    Thread-5 stop
    Thread-4 stop
    Thread-3 stop
    Thread-1 stop
    Thread-2 stop
    展开

    作者回复: 1 “可能”在 5 6 之前结束, 因为多线程是并发执行,没有限制它何时结束自己
    2 “可能”在5 6 之后结束,但是显示在了前面,因为产生结果是并行的,但是终端显示一定是串行的

    1
  • 2018-10-16
    多线程不能无限多吧,应该是看计算机的硬件性能。那么如何确定可以使用的最多线程?

    作者回复: 最多线程由两方面决定:内存容量和软件限制。
    虽然线程是轻量级进程,但是创建线程也是要消耗内存的,初始状态下消耗打小就是内存栈了,每创建一个线程为其分配一个线程栈;
    还有一种限制是系统的配置参数限制,比如在linux上 每进程默认创建线程是1024个,在local_lim.h中定义。可以使用ulimt -a查看线程栈大小

    1
  • 2018-06-26
    老师,我想问一下、那爬虫的多线程怎么实现啊,比如我的爬虫是:主页—所有城市—城市下所有游记
    写了五个方法,每个方法都要通过上层传的参数进行解析下层内容
     
    这时候多线程怎么实现呀

    作者回复: 实现的方法还是很多的,我介绍一种主流的方法给你,其实你也意识到从主页到下一级页面的不可拆解,其实是不需要将他们都进行多线程化的,多线程的目的是充分发挥爬虫主机的性能,和充分利用上传下载带宽不对等的空闲时间。
    可以考虑将获取需要下载游记的url作为一个方法,将这个方法多线程化,并发的获取url之后,将url存入一个临时的空间,再使用一个方法根据得到的url多线程化去请求网页并下载,既保证了采集url和下载的并发,又实现了异步,是不是设计的非常好?当然咱们同学也不用自己再开发这样的工具,有一个非常知名的爬虫框架叫做Scrapy就实现了这个功能,如果对多线程爬虫感兴趣建议学习一下这个框架和阅读它的例子

    1
  • 2019-11-03
    老师你好
    代码如下:
    import time
    import threading
    from threading import current_thread

    def prin(bl):
        time.sleep(3) # 让程序休眠3秒
        b=current_thread().getName()
        print('当前线程是: %s'%b)
        print(bl)

    for bl in range(1,10,1):
        s=threading.Thread(target=prin,args=bl)
        s.start()

    错误提示:
    Exception in thread Thread-1:
    Traceback (most recent call last):
      File "C:\Users\yangm\AppData\Local\Programs\Python\Python37\lib\threading.py", line 926, in _bootstrap_inner
        self.run()
      File "C:\Users\yangm\AppData\Local\Programs\Python\Python37\lib\threading.py", line 870, in run
        self._target(*self._args, **self._kwargs)
    TypeError: prin() argument after * must be an iterable, not int
    请问是哪里错了,我想不通。
    展开

    作者回复: 从报错信息来看,出错的位置在prin() 获取的参数必须是可迭代的,但是得到的是int,所以找到 s=threading.Thread(target=prin,args=bl)
    那么threading的Thread方法要求的函数怎么定义呢?
    import threading
    help(threading.Thread)
    __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)
     | *target* is the callable object to be invoked by the run()
      | *args* is the argument tuple for the target invocation. Defaults to ().

    args 要求参数是tuple (其实是可迭代),所以将此行改为s=threading.Thread(target=prin,args=(bl,)) 即可,提供个定位问题思路给大家

    1
  • 2019-09-01
    老师,有两个问题:
    1、Mythread类中只有一个run()方法,如果有多个方法,那么:
    t1 = Mythread()
    t1.start
    执行t1.start的时候,是同时执行多个方法吗?

    2、关于传参问题:
    t1 = Mythread(),能否像上一章节中传递arg1,arg2
    def myThread(arg1,arg2):
        print(current_thread().getName(),"start")
        print("%s %s"%(arg1,arg2))
        time.sleep(2)
        print(current_thread().getName(),"stop")
    如果可以传参,那么如何实现。
    展开

    作者回复: 1 start表示开始线程活动,默认会调用run()方法,视频演示的是重新了run方法,如果写多个其他方法start是不会去调用的

    2 直接传参就可以,但是因为Mythread类继承了threading.Thread,所以重写__init__的时候要记得初始化父类的__init__方法
    import threading

    class Mythread(threading.Thread):
        def __init__(self, property):
            super().__init__()
            self.property= property

        def run(self):
            print(self.property)

    if __name__ == '__main__':
        t1 = Mythread("666")
        t1.start()

    1
  • 2019-05-08
    def myThread(arg1,arg2):
        time.sleep(1)
        print('%s %s' % (arg1, arg2))
    结果:
    1 22 3
    3 4
    4 5
    5 6

    def myThread(arg1,arg2):
        time.sleep(1)
        print('%s %s' % (arg1, arg2))
    1 2
    2 3
    3 4
    4 5
    5 6
    sleep放在前面和放在后面完全不同的现象,感觉老师没有把知识点讲清楚,只是简单的举个小例子
    展开
  • 2019-04-24
    老师您好,按照您课上教学的代码,请问为什么您的输出好像都是很整齐的。
    而我的输出却如下很乱呢?
    Thread-1 start
    1 2
    Thread-2 start
    2 3Thread-3
     start
    Thread-43 4
    start
    Thread-54 5 MainThread
    startend

    5 6
    Thread-1 stop
    Thread-4Thread-3Thread-5Thread-2 stopstopstop


    stop
    展开

    作者回复: 您好,由于是多线程程序,输出内容不按顺序显示是正常现象

    1
  • 老师,我可以这么理解吗“并发的问题是指在多个请求过来,然后利用多线程编程使程序并行的运行”但这个并发指的是同一时刻有多个请求吗,还是指一段时间中有多个请求?

    作者回复: 不太准确,应该是说没有多线程时,计算机程序的每个步骤是按顺序执行的,如果这些步骤相互独立,没有因果关系,那么他们能充分利用计算机资源,就可以让他们并行运行起来。
    显然,同一时刻、同一时段不是区分是否该用多线程的关键,主要是看是否相互独立、是否没有因果关系。

  • 2019-04-05
    老师,您在最后提到控制线程结束的方法是t1.join(),并且先继承threading.Thread、重写了run()。但是我试着使用您前面不重写run(),直接将threading.Thread具体化到t1的方法,也可以通过t1.join()实现先结束t1所在的线程,最后再结束主线程。请问为什么要重写run呢?

    作者回复: 默认的run 是没有任何功能的, 你只有重写了run才能通过多线程实现你的功能啊, threading 模块是实现了多线程的框架,需要多线程做什么事情,要在run里面实现

  • 2019-03-21
    函数提示需要传入的参数类型,怎么显示的啊,在PyCharm里面。

    作者回复: 自动显示,如果没有显示请查看File- Power Save Mode(省电模式)是否已经打开了,将前面的“√”去掉即可

  • 2019-03-01
    import threading
    import time
    from threading import current_thread

    def myThread(arg1, arg2):
        print(current_thread().getName()+'_start')
        print('%s %s'%(arg1, arg2))
        time.sleep(1)
        print(current_thread().getName()+'_stop1')
        time.sleep(1)

    for i in range(1,6 1):
        t=threading.Thread(target=myThread, args=(i, i+1))
        t.start()
    t.join();
    print(current_thread().getName()+'_end')

    执行结果:
    Thread-2 start
    Thread-3 start
    Thread-4 start
    Thread-5 start
    Thread-1 stop
    Thread-2 stopThread-3 Thread-4 stopstop


    Thread-5 stop
    MainThread end
    这个stop是哪里来的呀而且没有下划线,难道是每次在pycharm里面执行有缓存么,我改了文件名输出结果都不变,麻烦老师帮看下最后为啥输出没有带下划线的stop字符串并且有的只单独的输出了stop字符串
    展开

    作者回复: 是否是代码文件上面有没注释的部分影响了你的输出呢?
    另一种可能是你运行的是整个工程,而不是当前的代码文件?在代码文件点击右键运行,再试一下

  • 2019-01-12
    t1.join在循环内:
    import threading
    from threading import current_thread
    import time
    class Mythread(threading.Thread):
       def run(self):
           print (current_thread().getName(),'start')
           t2 = time.ctime()
           print ("time is :%s" % t2)
           print (current_thread().getName(),'stop')
    for i in range(1,6):
        t1 = Mythread()
        t1.start()
        t1.join()

    print(current_thread().getName(),'End')
    ---------------------------
    Thread-1 start
    time is :Sat Jan 12 19:35:52 2019
    Thread-1 stop
    Thread-2 start
    time is :Sat Jan 12 19:35:52 2019
    Thread-2 stop
    Thread-3 start
    time is :Sat Jan 12 19:35:52 2019
    Thread-3 stop
    Thread-4 start
    time is :Sat Jan 12 19:35:52 2019
    Thread-4 stop
    Thread-5 start
    time is :Sat Jan 12 19:35:52 2019
    Thread-5 stop
    MainThread End

    t1.join在循环外:
    import threading
    from threading import current_thread
    import time
    class Mythread(threading.Thread):
       def run(self):
           print (current_thread().getName(),'start')
           t2 = time.ctime()
           print ("time is :%s" % t2)
           print (current_thread().getName(),'stop')
    for i in range(1,6):
        t1 = Mythread()
        t1.start()
    t1.join()

    print(current_thread().getName(),'End')
    ----------------------------------------
    Thread-1 start
    time is :Sat Jan 12 19:38:10 2019
    Thread-1 stop
    Thread-2 start
    time is :Sat Jan 12 19:38:10 2019
    Thread-2 stop
    Thread-3 start
    time is :Sat Jan 12 19:38:10 2019
    Thread-3 stop
    Thread-4 start
    time is :Sat Jan 12 19:38:10 2019
    Thread-4 stopThread-5
     start
    time is :Sat Jan 12 19:38:10 2019
    Thread-5 stop
    MainThread End
    好像没啥区别啊

    展开

    作者回复: 我想这个实验是验证是Julie同学的疑问的是吧,首先要清楚join()是解决子线程还没执行完,主线程就退出的问题。
    那问题来了为什么显示的结果没区别呢?其实是因为执行的是“空”代码,所以没来得及打印结果呢,主程序和多线程程序都结束了,两个看起来就是“一样”的了。
    试试改变程序为:
        def run(self):
            time.sleep(1)
            print(current_thread().getName(), 'start')
            time.sleep(1)
    增加time.sleep(1) 再对比一下?

  • 2019-01-05
    老师,我尝试了留言中的将t1.join()放在for里面和for外面,放在里面的话,就意味着每一个线程结束再执行下一个线程,最后结束主线程。放在外面的话,我一开始认为是结束了所有的子线程后才结束主线程,后来发现还是有线程会跑在主线程结束之后,后来我想是不是单独的t1.join()放在外面,应该是意味着只要有任意线程结束后,主线程就能退出,不知道是不是这么理解的?
    ps:后来度娘了一个方法,可以将所有线程名称收集起来,然后使用for方法去join(),这样就能等到所有的线程退出,再结束主线程
    展开
  • 2018-11-06
    问下老师,读取excel中的数字时为什么是float类型的,单元格设置成文本格式后也不行

    作者回复: python是支持强制格式转换的啊,如果需要将float转换为整数可以使用int()字符串可以用str()啊
    相信难不倒你的

  • 2018-10-16
    最后的 .join() 好像明白大致的作用:把所有的子线程先运行完,相当于“打包处理”的意思?

    然后,举的例子真的很不理解,多线程的例子变成的单个程序的执行,并不清楚到底多线程的 join() 到底是什么效果。
  • 2018-10-16
    我想问一个问题:

    如果把 run() 改写了,那么就无法实现target的函数功能了吧?是 print('run') 这里的内容改成函数么?

    作者回复: 按照演示的代码改写了run就不能使用target了。官方文档上说target是run的回调函数,啥意思呢?其实你看它们的源代码就明白了。以python3.7版本的threading.py为例,在第854行定义了run函数,里面这样写的:
            try:
                if self._target:
                    self._target(*self._args, **self._kwargs)
            finally:
                del self._target, self._args, self._kwargs

    run函数其实就是原原本本的调用了target=指定的函数。

  • 2018-10-16
    这节课的内容应该是要让我们明白单线程和多线程的区别,刚开始的第一个表现,两个程序都进行了,有了对比,比较明白,但是后面的演变,都是在多线程的框架下进行改变,没有单线程的演示,没有对比,不太会有两者良好的差别认识。