1、python多线程
#! /usr/bin/evn python3
# --*-- coding: utf-8 --*--
#该实例反编译来说明函数执行流程
import dis
def add(a):
a = a+1
return a
print(dis.dis(add))
# Python中一个线程对应于C语言中的一个线程(CPython而言)(Python并不一定就慢,视情况而定)
#pypy解释器专门克服gil慢的一种解释器(去gil化)
#GIL使用同一个时刻只有一个线程在一个cpu上执行字节码,无法将多个线程映射到多个CPU上
#gil锁会根据执行的字节码或时间片划分适当的释放(python内部实现机制)
#该实例来说明GIL在某种情况下会自动释放让下一个线程去执行(时间片来回切换)
#反编译(函数执行流程)同一时刻只有一个线程在CPU上执行
total = 0
def add():
global total
for i in range(1000000):
total += 1
def desc():
global total
for j in range(1000000):
total -= 1
import threading
threading1 = threading.Thread(target = add)
threading2 = threading.Thread(target= desc)
threading1.start()
threading2.start()
threading1.join()
threading2.join()
print(total)
#对于io操作来说,多线程和多进程差别不大(用两种方法实现Python多线程编写)
#1、通过Thread类实例化(适用简单的或是线程池)
#以模拟简单的爬取文章列表页在获取详情页作一示例
import time
import threading
def get_detail_html(url):
#爬取文章详情页
print("get detail html started")
time.sleep(2)
print("get detail html end")
def get_detail_url(url):
#爬取文章列表页
print("get url started")
time.sleep(4)
print("get detail url end")
if __name__=="__main__":
thread1 = threading.Thread(target=get_detail_html,args=("",)) #线程1
thread2 = threading.Thread(target=get_detail_url,args=("",)) #线程2
# thread1.setDaemon(True) #守护线程
# thread2.setDaemon(True) #守护线程
start_time = time.time()
thread1.start()
thread2.start()
# thread1.join() #阻塞等待
# thread2.join() #阻塞等待
print("last time: {}".format(time.time()-start_time)) #主线程
#2、通过集成Thread来实现多线程
import threading
import time
class GetDetailHtml(threading.Thread):
def __init__(self,name): #重写__init__方法
super().__init__(name=name) #调用__init__方法
def run(self): #重写run方法,而非start方法
print("get detail html")
time.sleep(2)
print("get html end")
class GetDetailUrl(threading.Thread):
def __init__(self,name): #重写__init__方法
super().__init__(name=name) #调用__init__方法
def run(self): #重写run方法,而非start方法(在此可以编写逻辑复杂的程序)
print("get detail url")
time.sleep(4)
print("get url end")
if __name__=="__main__":
thread1 = GetDetailHtml("get_detail_html")
thread2 = GetDetailUrl("get_detail_url")
start_time = time.time()
thread1.start()
thread2.start()
thread1.join() #阻塞等待回收
thread2.join() ##阻塞等待回收
#当主线程退出的时候,子线程kill掉
print("last time: {}".format(time.time()-start_time))
2、线程间的通信方式–共享变量
#!/usr/bin/evn python3
# --*-- coding: utf-8 --*--
#线程之间的通信
# 1、线程间的通信方式--共享变量(不推荐)
# 如果是各种数据的时候,也可首选使用共享变量而非queue
#共享变量的操作并不是线程安全的操作,为了达到预期的效果必须在这些操作上加上一把锁,能够安照预期的效果在线程之间按照顺序进行同步
#多进程中共享变量是行不通的
#声明一个全局变量,将这个全局变量在各个线程中使用
#以模拟简单的爬取文章列表页在获取详情页作一示例
import time
import threading
#设置全局变量的方式
detail_url_list = [] #作用:获取文章的列表页并获取文章详情页的url
# (该列表(或全局或全局变量)可以定义在.py文件中,直接from 模块 import xx (xx.py)--> xx.全局变量)
#from chaper11 import variables 不推荐:from chapter11.variables import detail_url_list
# detail_url_list = variables.detail_url_list
#这种方式是通过声明全局变量global的方式进行通信,非常原始并且不够灵活
def get_detail_html():
#爬取文章详情页
global detail_url_list
while True:
if len(detail_url_list):
url = detail_url_list.pop()
# for url in detail_url_list:
print("get detail html started")
time.sleep(2)
print("get detail html end")
def get_detail_url():
global detail_url_list
while True:
#爬取文章列表页
print("get url started")
time.sleep(2)
for i in range(20):
detail_url_list.append("http://projectstedu.com/{id}".format(id=i))
print("get detail url end")
if __name__=="__main__":
thread_detail_url = threading.Thread(target=get_detail_url) #线程1
thread_detail_url.start()
for i in range(10):
html_thread = threading.Thread(target=get_detail_html)
html_thread.start()
start_time = time.time()
# 当主线程退出的时候,子线程kill掉
print("last time: {}".format(time.time() - start_time))
#根据上面进行变形后的程序
#以模拟简单的爬取文章列表页在获取详情页作一示例
import time
import threading
#设置引用的方式
detail_url_list = [] #作用:获取文章的列表页并获取文章详情页的url
# (该列表(或全局或全局变量)可以定义在.py文件中,直接from 模块 import xx (xx.py)--> xx.全局变量)
#from chaper11 import variables 不推荐:from chapter11.variables import detail_url_list
# detail_url_list = variables.detail_url_list
#这种方式是通过引用变量参数的方式进行通信,足够灵活
def get_detail_html(detail_url_list): #传入引用,较灵活的方法
#爬取文章详情页
while True:
# global detail_url_list(去掉全局变量)
if len(detail_url_list):
url = detail_url_list.pop()
# for url in detail_url_list:
print("get detail html started")
time.sleep(2)
print("get detail html end")
def get_detail_url(detail_url_list): #传入引用,较灵活的方法
# global detail_url_list (去掉全局变量)
#爬取文章列表页
while True:
print("get url started")
time.sleep(4)
for i in range(20):
detail_url_list.append("http://projectstedu.com/{id}".format(id=i))
print("get detail url end")
if __name__=="__main__":
thread_detail_url = threading.Thread(target=get_detail_url,args=(detail_url_list,)) #线程1
thread_detail_url.start()
for i in range(10):
html_thread = threading.Thread(target=get_detail_html,args=(detail_url_list,))
html_thread.start()
start_time = time.time()
# 当主线程退出的时候,子线程kill掉
print("last time: {}".format(time.time() - start_time))
3、线程间的通信方式–通过Queue模块进行线程间同步
#!/usr/bin/evn python3
# --*-- coding: utf-8 --*--
#1、线程间的通信方式--通过queue的方式进行线程间同步(推荐)
# 线程间需要通信,使用全局变量需要加锁。
# 使用queue模块,可在线程间进行通信,并保证了线程安全。
#以模拟简单的爬取文章列表页在获取详情页作一示例
# queue是线程安全,不加锁,效率高,因为queue用了python中的deque() 双端队列,而deque()则是线程安全的,在字节码的级别上就已经达到了线程安全
from queue import Queue
import time
import threading
#设置引用的方式
detail_url_list = [] #作用:获取文章的列表页并获取文章详情页的url
# (该列表(或全局或全局变量)可以定义在.py文件中,直接from 模块 import xx (xx.py)--> xx.全局变量)
#from chaper11 import variables 不推荐:from chapter11.variables import detail_url_list
# detail_url_list = variables.detail_url_list
#这种方式是通过引用变量参数的方式进行通信,足够灵活
def get_detail_html(queue): #传入引用,较灵活的方法
#爬取文章详情页
while True:
url = queue.get()
# for url in detail_url_list:
print("get detail html started")
time.sleep(2)
print("get detail html end")
def get_detail_url(queue): #传入引用,较灵活的方法
# global detail_url_list (去掉全局变量)
#爬取文章列表页
while True:
print("get url started")
time.sleep(4)
for i in range(20):
queue.put("http://projectstedu.com/{id}".format(id=i)) #阻塞等待有空闲空间为止(put,参数block默认为True,阻塞状态,可以设置timeout)
print("get detail url end")
if __name__=="__main__":
detail_url_queue = Queue(maxsize=1000)
thread_detail_url = threading.Thread(target=get_detail_url,args=(detail_url_list,)) #线程1
thread_detail_url.start()
for i in range(10):
html_thread = threading.Thread(target=get_detail_html,args=(detail_url_list,))
html_thread.start()
# detail_url_queue.task_done() 调用task_done()函数join()函数才会退出,停止退出的作用
# detail_url_queue.join() 阻塞等待
start_time = time.time()
# 当主线程退出的时候,子线程kill掉
print("last time: {}".format(time.time() - start_time))