Python3 threading 多线程实战

主题

正常情况下,程序的运行按顺序执行,但是涉及某些操作,等待结果完成却是非常耗时的操作,比如爬虫进行IO操作等,当涉及的量较大的时候,同步执行的程序十分的耗时,为了使得支持并发操作,缩减程序运行的时间,提高效率。多线程技术就是为了实现这样的功能。

多线程编程中涉及到许多概念:

并发式编程
多线程和多进程
线程安全
线程的声明周期
线程的类型
创建线程
线程间的通信
事件通知
锁和可重入锁
线程挂起
实战型演示多线程编程,并列出我认为写的好的参考文献,供大家参考。

0.

全文思路

主要涉及的python模块是:threading

1.

threading

函数
类包装线程对象:实现run方法
Queue:实现数据间的通信,生产者和消费者模式

Python
# 1 创建新线程:<span class="wp_keywordlink_affiliate"><a href="https://www.168seo.cn/tag/threading-thread" title="View all posts in threading.Thread" target="_blank">threading.Thread</a></span>(func, args=(,)) 启动线程活动:start() 等待至线程终止:join() # 2 生产者-消费者模型 Queue.put() Queue.get() # 3 互斥锁同步:数据共享;当多个线程都修改某一个共享数据的时候,需要进行同步控制。 线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性 # 创建锁 mutex = <span class="wp_keywordlink_affiliate"><a href="https://www.168seo.cn/tag/threading" title="View all posts in threading" target="_blank">threading</a></span>.Lock() # 锁定 mutex.acquire([timeout]) # 释放 mutex.release() # 4 死锁:在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。 # 5 可重入锁:一个线程“迭代”请求同一个资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 1
创建新线程: threading . Thread ( func , args = ( , ) )
启动线程活动: start ( )
等待至线程终止: join ( )
 
# 2
生产者 -消费者模型
Queue . put ( )
Queue . get ( )
 
# 3
互斥锁同步:数据共享 ;当多个线程都修改某一个共享数据的时候,需要进行同步控制。
 
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定 /非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性
# 创建锁
mutex = threading . Lock ( )
# 锁定
mutex . acquire ( [ timeout ] )
# 释放
mutex . release ( )
 
# 4
死锁:在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
 
# 5
可重入锁:一个线程“迭代”请求同一个资源

2.目标网站:

目标:获取一年内的停复牌信息

目标网址:http://www.cninfo.com.cn/cninfo-new/memo-2?queryDate=2016-11-17&queryType=queryType1

网址和日期相关
周末无数据
获取11月份有效url的方法:

不知道为啥我写的这么复杂又是回调函数,又是各种函数调用。

Python
def crawlurl(year=2016, month=11, day=None, <span class="wp_keywordlink_affiliate"><a href="https://www.168seo.cn/tag/queue" title="View all posts in queue" target="_blank">queue</a></span>=None, *, callback, lastcall): days = [i for i in range(1, 31)] for day in days: if callback(year, month, day=day): data = datetime(year, month, day) URL = lastcall(data) <span class="wp_keywordlink_affiliate"><a href="https://www.168seo.cn/tag/queue" title="View all posts in queue" target="_blank">queue</a></span>.put(("url", URL)) # 判断日期是否有效,周末和未来网站无数据 def is_valid_day(year=2016, month=11, day=None): data = datetime(year=year, month=month, day=day) if data.weekday() in range(0, 5) and data <= datetime.today(): return True # 构造url方法 def geturl(data): url = "http://www.cninfo.com.cn/cninfo-new/memo-2?queryDate=%s&queryType=queryType1" return url % changedatetime(data) # 将日期格式化成2016-11-17形式 def changedatetime(date): return date.strftime('%Y-%m-%d') A = Queue() allurl = crawlurl(2016,11, queue= A, callback=is_valid_day, lastcall=geturl) print(A.qsize()) # 截止17号,网站存在13个有效日期:去除5,6,12,13周末日 A.put(("None", -1)) while True: _, url = A.get() if url == -1: break print(url)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def crawlurl ( year = 2016 , month = 11 , day = None , queue = None , * , callback , lastcall ) :
     days = [ i for i in range ( 1 , 31 ) ]
     for day in days :
         if callback ( year , month , day = day ) :
             data = datetime ( year , month , day )
             URL = lastcall ( data )
             queue . put ( ( "url" , URL ) )
 
# 判断日期是否有效,周末和未来网站无数据
def is_valid_day ( year = 2016 , month = 11 , day = None ) :
     data = datetime ( year = year , month = month , day = day )
     if data . weekday ( ) in range ( 0 , 5 ) and data <= datetime . today ( ) :
         return True
 
# 构造url方法
def geturl ( data ) :
     url = "http://www.cninfo.com.cn/cninfo-new/memo-2?queryDate=%s&queryType=queryType1"
     return url % changedatetime ( data )
 
# 将日期格式化成2016-11-17形式
def changedatetime ( date ) :
     return date . strftime ( '%Y-%m-%d' )
 
A = Queue ( )
allurl = crawlurl ( 2016 , 11 , queue = A , callback = is_valid_day , lastcall = geturl )
print ( A . qsize ( ) ) # 截止17号,网站存在13个有效日期:去除5,6,12,13周末日
A . put ( ( "None" , - 1 ) )
while True :
     _ , url = A . get ( )
     if url == - 1 :
         break
     print ( url )

一、实例化Thread类来使用多线程

Thread(target=func, args=())

Python
# url是上文获取的url, queue 用来将IO操作抓取的网页信息存入队列中 def download(url, queue): headers = {"Host": "www.cninfo.com.cn", "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36"} try: resp = requests.get(url, headers=headers) if resp.status_code == 200: queue.put(("response", resp.text)) except: print("Connection wrong")
1
2
3
4
5
6
7
8
9
10
# url是上文获取的url, queue 用来将IO操作抓取的网页信息存入队列中
def download ( url , queue ) :
     headers = { "Host" : "www.cninfo.com.cn" ,
                 "User-Agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36" }
     try :
         resp = requests . get ( url , headers = headers )
         if resp . status_code == 200 :
             queue . put ( ( "response" , resp . text ) )
     except :
         print ( "Connection wrong" )
Python
# 这里整体的思路是: # 1. 将获取的url存放在url_queue队列中 # 2. 将url_queue队列中的url 传入下载函数中 # 3. 将获取的网页信息数据存入:response_queue队列中 url_queue = Queue() response_queue = Queue() i = crawlurl(2016,11, queue= url_queue, callback=is_valid_day, lastcall=geturl) print(url_queue.qsize()) # 13 url_queue.put(("None", -1)) # 插入标志信息好让函数返回 <span class="wp_keywordlink_affiliate"><a href="https://www.168seo.cn/tag/thread" title="View all posts in thread" target="_blank">thread</a></span>s = [] while True: _, url = url_queue.get() if url == -1: break t = Thread(target=download, args=(url, response_queue)) <span class="wp_keywordlink_affiliate"><a href="https://www.168seo.cn/tag/thread" title="View all posts in thread" target="_blank">thread</a></span>s.append(t) t.start() # 启动线程 for j in threads: j.join() # 线程挂起,主线程会等待子线程全部完成才继续运行 print(response_queue.qsize()) # 13 response_queue.put(("None", -1)) while True: _, response = response_queue.get() if response == -1: break print(len(response)) # 查看是否真的获取网页信息 #33087 #36111 #39310 #43046 #36163 #40661 #42014 #48080 #52088 #34507 #40294 #28014 #44018
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 这里整体的思路是:
 
# 1. 将获取的url存放在url_queue队列中
# 2. 将url_queue队列中的url 传入下载函数中
# 3. 将获取的网页信息数据存入:response_queue队列中
 
url_queue = Queue ( )
response_queue = Queue ( )
i = crawlurl ( 2016 , 11 , queue = url_queue , callback = is_valid_day , lastcall = geturl )
print ( url_queue . qsize ( ) ) # 13
url_queue . put ( ( "None" , - 1 ) ) # 插入标志信息好让函数返回
 
threads = [ ]
while True :
     _ , url = url_queue . get ( )
     if url == - 1 :
         break
     t = Thread ( target = download , args = ( url , response_queue ) )
     threads . append ( t )
     t . start ( )    # 启动线程
for j in threads :
     j . join ( )      # 线程挂起,主线程会等待子线程全部完成才继续运行
 
print ( response_queue . qsize ( ) )    # 13
response_queue . put ( ( "None" , - 1 ) )
 
while True :
     _ , response = response_queue . get ( )
     if response == - 1 :
         break
     print ( len ( response ) )    # 查看是否真的获取网页信息
#33087
#36111
#39310
#43046
#36163
#40661
#42014
#48080
#52088
#34507
#40294
#28014
#44018

二、继承Thread类,重写run方法

Python
class Download(Thread): def __init__(self, url, queue): super().__init__() self.url = url self.queue = queue def run(self): response = self.download() self.queue.put(("response", response)) def download(self): headers = {"Host": "www.cninfo.com.cn", "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36"} try: resp = requests.get(self.url, headers=headers) if resp.status_code == 200: return resp.text except: print("Connection wrong") # 获取url url_queue = Queue() response_queue = Queue() i = crawlurl(2016,11, queue= url_queue, callback=is_valid_day, lastcall=geturl) print(url_queue.qsize()) url_queue.put(("None", -1)) threads2 = [] response_queue2 = Queue() while True: _, url = url_queue.get() if url == -1: break t2 = Download(url, response_queue2) threads2.append(t2) for i in threads2: i.start() for j in threads2: j.join() print(response_queue2.qsize()) # 13 response_queue2.put(("None", -1)) while True: _, response = response_queue2.get() if response == -1: break print(len(response)) #33087 #34507 #28014 #44018 #48080 #36111 #43046 #36163 #40294 #40661 #39310 #52088 #42014
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class Download ( Thread ) :
     def __init__ ( self , url , queue ) :
         super ( ) . __init__ ( )
         self . url = url
         self . queue = queue
 
     def run ( self ) :
         response = self . download ( )
         self . queue . put ( ( "response" , response ) )
 
     def download ( self ) :
         headers = { "Host" : "www.cninfo.com.cn" ,
                   "User-Agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36" }
         try :
             resp = requests . get ( self . url , headers = headers )
             if resp . status_code == 200 :
                 return resp . text
         except :
             print ( "Connection wrong" )
# 获取url
url_queue = Queue ( )
response_queue = Queue ( )
i = crawlurl ( 2016 , 11 , queue = url_queue , callback = is_valid_day , lastcall = geturl )
print ( url_queue . qsize ( ) )
url_queue . put ( ( "None" , - 1 ) )
 
threads2 = [ ]
response_queue2 = Queue ( )
while True :
     _ , url = url_queue . get ( )
     if url == - 1 :
         break
     t2 = Download ( url , response_queue2 )
     threads2 . append ( t2 )
for i in threads2 :
     i . start ( )
for j in threads2 :
     j . join ( )
print ( response_queue2 . qsize ( ) ) # 13
response_queue2 . put ( ( "None" , - 1 ) )
while True :
     _ , response = response_queue2 . get ( )
     if response == - 1 :
         break
     print ( len ( response ) )
 
#33087
#34507
#28014
#44018
#48080
#36111
#43046
#36163
#40294
#40661
#39310
#52088
#42014



  • zeropython 微信公众号 5868037 QQ号 5868037@qq.com QQ邮箱
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值