科技在发展,时代在进步,我们的CPU也越来越快,CPU抱怨,P大点事儿占了我一定的时间,其实我同时干多个活都没问题的;于是,操作系统就进入了多任务时代。我们听着音乐吃着火锅的不在是梦想。
python提供了两个模块来实现多线程thread 和threading ,thread 有一些缺点,在threading 得到了弥补,为了不浪费你和时间,所以我们直接学习threading 就可以了。
1.代码:
#coding: utf-8
import threading
import time
class Foo(threading.Thread):
def run(self):
print '我开始听音乐', '\n'
time.sleep(1)
print '我已经听完了音乐', '\n'
def foo():
print '我开始吃火锅', '\n'
time.sleep(2)
print '我已经吃完了火锅', '\n'
t = Foo()
d = threading.Thread(target = foo)
t.start()
# d.setDaemon(True)
d.start()
1.png我们先听了一首音乐,通过Foo类来控制音乐的播放,音乐播放需要1秒钟,sleep()来控制音乐播放的时长。同时我们又看了一场电影,每一场电影需要2秒钟。输出如下图:
2. setDaemon(True):守护线程
我们在标题1的代码里面进行一些修改来体现出setDaemon的作用
#coding: utf-8
import threading
import time
class Foo(threading.Thread):
def run(self):
print '我开始听音乐', '\n'
time.sleep(1)
print '我已经听完了音乐', '\n'
def foo():
print '我开始吃火锅', '\n'
time.sleep(2)
print '我已经吃完了火锅', '\n'
t = Foo()
d = threading.Thread(target = foo)
t.start()
d.setDaemon(True)
d.start()
2.png这里面d给设置了守护线程,2个线程同时开始,但是如果其他线程结束了,那么守护线程不管有没有完成都要结束。意思就是我们听着音乐,吃着火锅,音乐听完了,不管我们火锅有没有吃完,就得赶紧撤了。
下图是输出结果:
3.join()阻塞
继续修改我们的代码
#coding: utf-8
import threading
import time
class Foo(threading.Thread):
def run(self):
print '我开始听音乐', '\n'
time.sleep(1)
print '我已经听完了音乐', '\n'
def foo():
print '我开始吃火锅', '\n'
time.sleep(2)
print '我已经吃完了火锅', '\n'
t = Foo()
d = threading.Thread(target = foo)
t.start()
d.setDaemon(True)
d.start()
d.join()
3.png我们将d进行阻塞,就是t执行完成,你才能执行d,也就是我们必须听完音乐,才能看电影 。
下图是输出:
4.queue队列
队列的意思是将东西放进去,可以随时取出来,下面有个代码演示:
#coding: utf-8
import Queue
q = Queue.Queue()
for i in range(5):
q.put(i)
while not q.empty():
print q.get()
输出结果:
4.png这个应该很容易理解,就不用多解释。
5.使用过多线程进行网页爬取
代码如下:
#!/usr/bin/env python
# coding: utf-8
#队列自己带锁,自己阻塞,线程安全的,不用自己显式的去编写
import threading
import requests
import urllib
import Queue
from bs4 import BeautifulSoup
class Foo(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def run(self):
while True:
key = self.queue.get()#取key列表里面的元素
self.baidu(key)#运行函数baidu,传的参数是key
self.queue.task_done()#取不到元素的时候自动退出程序
def baidu(self, keys):
gjc = urllib.quote(keys)
url = 'https://www.baidu.com/s?ie=utf-8&mod=1&isid=C72D6237C6C55642&ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=' + gjc + '&rsv_pq=816a886000062c06&rsv_t=c589dMGIygeVyzfmHXpCrPrWN2S4yWp8ttSNQ77uzbcec5H1cOVF2yiedcs&rqlang=cn&rsv_enter=1&rsv_sug3=7&rsv_sug1=3&rsv_sug7=100&rsv_sug2=0&inputT=89&rsv_sug4=41058&rsv_sid=1424_21119_17001_22072&_ss=1&clist=&hsug=&f4s=1&csor=2&_cr1=28095'
header = { 'Refer':'https://www.baidu.com/',
'Host':'www.baidu.com',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36',
'Cookie':'BAIDUID=75774ED511BF2DECB6C4A142D1F4CCDF:FG=1; PSTM=1499515257; BIDUPSID=DD4A04E2ED6C099A9F40EF86229306AB; FP_UID=616a141a155bdb648b37b61ff16cfdc3; BDRCVFR[yfg8b4Gp7xm]=yiTPYW-i3eTXZFWmv68mvqV; H_PS_645EC=9607I22jkjSFIiy14y6B2VlNt4TQHDqMpVs%2FNy6mdneG1NMGExHCLS0uRrs; BD_CK_SAM=1; PSINO=5; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BD_HOME=0; H_PS_PSSID=1447_19033_13550_21095_18560_17001_22160; BD_UPN=12314753'
}
content = requests.get(url, headers=header).content
soup = BeautifulSoup(content, 'html.parser')
keyword = soup.find_all('div', id="rs")
for i in keyword:
print i.get_text()
if __name__ == '__main__':
queue = Queue.Queue()
key = ['苹果8', '小米Mx2', '华为mate10']
for i in key:
queue.put(i)#将key加入队列里面,用于run()的调用
for item in range(3):
t = Foo(queue)
t.setDaemon(True)#守护进程,如果主线程结束,则会退出
t.start()
queue.join()#阻塞,函数baidu('苹果8')执行完成,才会继续进行下一次baidu('小米Mx2')
输出结果:
5.png6.python中多线程的优缺点
多线程目前仅用于网络多线程采集, 以及性能测试。
其它的语言也有类似的情况,线程本身的特点导致线程的适用范围是受限的。只有CPU过剩,而其它的任务很慢,此时用线程才是有益的,可以很好平衡等待时间,提高并发性能。线程的问题主要是线程的安全稳定性。线程无法强制中止,同时线程与主进程共享内存,可能会影响主进程的内存管理。在python里线程出问题,可能会导致主进程崩溃。 虽然python里的线程是操作系统的真实线程。那么怎么解决呢?通过我们用进程方式。子进程崩溃后,会完全的释放所有的内存和错误状态。所以进程更安全。 另外通过进程,python可以很好的绕过GIL,这个全局锁问题。但是进程也是有局限的。不要建立超过CPU总核数的进程,否则效率也不高。简单的总结一下。当我们想实现多任务处理时,首先要想到使用multiprocessing, 但是如果觉着进程太笨重,那么就要考虑使用线程。 如果多任务处理中需要处理的太多了,可以考虑多进程,每个进程再采用多线程。如果还处理不要,就要使用轮询模式,比如使用poll event, twisted等方式。如果是GUI方式,则要通过事件机制,或者是消息机制处理,GUI使用单线程。所以在python里线程不要盲目用, 也不要滥用。 但是线程不安全是事实。如果仅仅是做几个后台任务,则可以考虑使用守护线程做。如果需要做一些危险操作,可能会崩溃的,就用子进程去做。 如果需要高度稳定性,同时并发数又不高的服务。则强烈建议用多进程的multiprocessing模块实现。
网友评论