多任务及线程的创建
1.先谈多任务,假如没有多任务,那就只能执行完一个任务再去执行一个任务。
先看代码:
import time
import threading
def sing():
for i in range(1,5):
print("%s在唱歌"%i)
print("sing end-----")
def dance():
for i in range(1,5):
print("%s在跳舞" % i)
print("dance end-------")
if __name__ == '__main__':
sing()
dance()
结果:
其实,正常的情况下应该边唱歌边跳舞。
2.多任务实现线程:
当为单核cpu的时候,其实是假的多任务,看似同时执行。其实一种方法是通过操作系统调度,让任务的切换时间足够快,让用户认为几乎是在同一时间执行的。另外一种方法为优先级调度,它对时间的要求不严格。
多任务涉及两个概念:并行和并发,解释一下,并行指的是cpu的核数大于任务数,并发是指cpu核数小于任务数。
线程
- 一个程序运行起来后,一定有一个执行代码的东西,这个东西就是线程。
- 线程运行没有顺序。
- 当创建的子线程所执行的函数结束,那么意味着这个子线程结束。
- 如果创建的主线程死掉,那么子线程一定会死掉。
1.如何查看线程的数量
threading.enumerate()
threading.enumerate()返回的是一个列表,包括主线程
调用thread,但没有执行start()方法那么仅仅是创建了一个对象,只有调用start()方法时,才会创建线程,以及让线程开始运行。
2.创建线程的方式
- 使用threading.Thread(target=函数名,args=(元组参数))
- 创建线程类,继承threading.Thread,且必须包含run()方法。当一个线程做的事情比较复杂的时候,而且需要分成多个函数来实现功能。例如层级爬取数据可以考虑使用创建线程类。
上代码1(使用threading.Thread):
import time
import threading
def sing():
for i in range(1,5):
print("%s在唱歌"%i)
# time.sleep(1)
print("sing end-----")
def dance():
for i in range(1,5):
print("%s在跳舞" % i)
# time.sleep(1)
print("dance end-------")
if __name__ == '__main__':
start=time.time()
sing()
dance()
t1=threading.Thread(target=sing)
t2=threading.Thread(target=dance)
t1.start()
end=time.time()
print('time is %s'%(start-end))
while True:
length=len(threading.enumerate())
print("当前运行的线程为:%d"%length)
if length<=1:
break
print(threading.enumerate())
time.sleep(1)
代码2(创建线程类)爬取腾讯招聘:
import json
import time
import requests
import threading
from queue import Queue
class Tecentthead(threading.Thread):
def __init__(self,i,page_queue):
super().__init__()
self.i=i
self.page_queue=page_queue
def run(self):
#线程不能执行一个任务就退出,需要不断取任务
# 所以可以通过队列是否为空来保证任务都被执行
while True:
#任务停止条件,当任务为空时,线程退出
if self.page_queue.empty():
break
page = page_queue.get()
print("===================线程{}开始执行任务{}================".format(self.i, page))
url='https://careers.tencent.com/tencentcareer/api/post/Query?keyword=python&pageIndex={}&pageSize=10'.format(page)
self.get_info(url)
print("===================线程{}执行任务{}结束================".format(self.i,page))
#1.请求接口数据
def get_info(self,url):
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}
response = requests.get(url=url, headers=headers).text
# with open('tecent.json','w',encoding='utf-8')as f :
# f.write(response)
self.parse(response)
#2.解析接口数据
def parse(self,response):
response = json.loads(response)
# print(type(response))
joblist = response["Data"]["Posts"]
for i in joblist:
name = i["RecruitPostName"]
LocationName = i["LocationName"]
Responsibility = i["Responsibility"].replace("\n", "").replace("\r", "")
PostURL = i["PostURL"]
info = "职位:{},工作地点{},职责:{},链接:{}".format(name, LocationName, Responsibility, PostURL)
with open('job1.txt', 'a', encoding='utf-8')as f:
f.write(info + "\n")
if __name__ == '__main__':
#主进程的任务就是启动线程开关,并不担心任务执行过程。
start_time=time.time()
print("============主线程开始,时间为{}===============".format(start_time))
#1.创建任务队列
page_queue=Queue()
for page in range(1,10):
page_queue.put(page)
#2.起线程任务,规定3个线程
threadjob_list=['c1','c2','c3']
#3.创建任务队列,用来阻塞主线程的结束
job_list=[]
for i in threadjob_list:
t=Tecentthead(i,page_queue)
t.start()
job_list.append(t)
#循环遍历任务队列,阻塞主进程的结束,等任务队列中的所有任务结束之后
#主进程最后结束。join的主要作用就是阻塞主进程的结束,优先执行自己。
for k in job_list:
k.join()
end_time=time.time()
print("============主线程结束,耗时为{}===============".format(end_time-start_time))