作者回复:
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()来运行,这就是他们两个的区别了。
作者回复: 做这样一个实验,thread.join()这条语句放在for循环中,放在for循环外(即这条语句前面没有空格),注释这条语句,观察一下输出有什么变化?你能解释三种变化的原因吗?
作者回复: 1 “可能”在 5 6 之前结束, 因为多线程是并发执行,没有限制它何时结束自己
2 “可能”在5 6 之后结束,但是显示在了前面,因为产生结果是并行的,但是终端显示一定是串行的
作者回复: 最多线程由两方面决定:内存容量和软件限制。
虽然线程是轻量级进程,但是创建线程也是要消耗内存的,初始状态下消耗打小就是内存栈了,每创建一个线程为其分配一个线程栈;
还有一种限制是系统的配置参数限制,比如在linux上 每进程默认创建线程是1024个,在local_lim.h中定义。可以使用ulimt -a查看线程栈大小
作者回复: 实现的方法还是很多的,我介绍一种主流的方法给你,其实你也意识到从主页到下一级页面的不可拆解,其实是不需要将他们都进行多线程化的,多线程的目的是充分发挥爬虫主机的性能,和充分利用上传下载带宽不对等的空闲时间。
可以考虑将获取需要下载游记的url作为一个方法,将这个方法多线程化,并发的获取url之后,将url存入一个临时的空间,再使用一个方法根据得到的url多线程化去请求网页并下载,既保证了采集url和下载的并发,又实现了异步,是不是设计的非常好?当然咱们同学也不用自己再开发这样的工具,有一个非常知名的爬虫框架叫做Scrapy就实现了这个功能,如果对多线程爬虫感兴趣建议学习一下这个框架和阅读它的例子
作者回复: 从报错信息来看,出错的位置在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 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()
作者回复: 您好,由于是多线程程序,输出内容不按顺序显示是正常现象
作者回复: 不太准确,应该是说没有多线程时,计算机程序的每个步骤是按顺序执行的,如果这些步骤相互独立,没有因果关系,那么他们能充分利用计算机资源,就可以让他们并行运行起来。
显然,同一时刻、同一时段不是区分是否该用多线程的关键,主要是看是否相互独立、是否没有因果关系。
作者回复: 默认的run 是没有任何功能的, 你只有重写了run才能通过多线程实现你的功能啊, threading 模块是实现了多线程的框架,需要多线程做什么事情,要在run里面实现
作者回复: 自动显示,如果没有显示请查看File- Power Save Mode(省电模式)是否已经打开了,将前面的“√”去掉即可
作者回复: 是否是代码文件上面有没注释的部分影响了你的输出呢?
另一种可能是你运行的是整个工程,而不是当前的代码文件?在代码文件点击右键运行,再试一下
作者回复: 我想这个实验是验证是Julie同学的疑问的是吧,首先要清楚join()是解决子线程还没执行完,主线程就退出的问题。
那问题来了为什么显示的结果没区别呢?其实是因为执行的是“空”代码,所以没来得及打印结果呢,主程序和多线程程序都结束了,两个看起来就是“一样”的了。
试试改变程序为:
def run(self):
time.sleep(1)
print(current_thread().getName(), 'start')
time.sleep(1)
增加time.sleep(1) 再对比一下?
作者回复: python是支持强制格式转换的啊,如果需要将float转换为整数可以使用int()字符串可以用str()啊
相信难不倒你的
作者回复: 按照演示的代码改写了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=指定的函数。