人活在世上永远如临深渊,其实这世上从来就没有回头二字,最多也只是重新开始。
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务,多线程就是在一个进程中的多个线程,如果使用多线程默认开启一个主线程,按照程序需求自动开启多个线程(也可以自己定义线程数)。
多线程知识点
- Python 在设计之初就考虑到要在解释器的主循环中,同时只有一个线程在执行,即在任意时刻,只有一个线程在解释器中运行。对Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
- 多线程共享主进程的资源,所以可能还会改变其中的变量,这个时候就要加上线程锁,每次执行完一个线程在执行下一个线程。
- 因为每次只能有一个线程运行,多线程怎么实现的呢?Python解释器中一个线程做完了任务然后做IO(文件读写)操作的时候,这个线程就退出,然后下一个线程开始运行,循环之。
- 当你读完上面三点你就知道多线程如何运行起来,并且知道多线程常用在那些需要等待然后执行的应用程序上(比如爬虫读取到数据,然后保存的时候下一个线程开始启动)也就是说多线程适用于IO密集型的任务量(文件存储,网络通信)。
- 注意一点,定义多线程,然后传递参数的时候,如果是有一个参数就是用args=(i,)一定要加上逗号,如果有两个或者以上的参数就不用这样。
代码实例
案例一 多线程核心用法
import sys
import threading
import time
reload(sys)
sys.setdefaultencoding('utf-8')
def loop():
#定义一个要循环的函数,当然后面肯定会定义好几个函数
print 'thread %s is running...' % threading.current_thread().name
#threading.current_thread().name就是当前线程的名字 在声明线程的时候可以自定义子线程的名字
n = 0
while n < 10:
n = n + 1
print '%s >>> %s' % (threading.current_thread().name, n)
#输出当前线程名字 和循环的参数n
print 'thread %s ended.' % threading.current_thread().name
print 'thread %s is running...' % threading.current_thread().name
#下面的一部分就是threading的核心用法
#包括target name args 之类的 一般我只用targer=你定义的函数名
t = threading.Thread(target=loop, name='线程名:')
# 在这里就申明了这个线程的名字
t.start()
#开始
t.join()
#关于join的相关信息我会在后面的代码详说
print 'thread %s ended.' % threading.current_thread().name
运行结果:
thread MainThread is running...
thread 线程名: is running...
线程名: >>> 1
线程名: >>> 2
线程名: >>> 3
线程名: >>> 4
线程名: >>> 5
线程名: >>> 6
线程名: >>> 7
线程名: >>> 8
线程名: >>> 9
线程名: >>> 10
thread 线程名: ended.
thread MainThread ended.
案例二 线程锁
前面有说到过,多线程是共享内存的,所以其中的变量如果发生了改变的话就会改变后边的变量,导致异常,这个时候可以加上线程锁。线程锁的概念就是主要这个线程运行完后再运行下一个线程。
import sys
import threading
import time
reload(sys)
sys.setdefaultencoding('utf-8')
def loop():
l.acquire()
# 这里相当于把线程加了锁,目前只允许这一个线程运行
print 'thread %s is running...' % threading.current_thread().name
#threading.current_thread().name就是当前线程的名字 在声明线程的时候可以自定义子线程的名字
n = 0
while n < 10:
n = n + 1
print '%s >>> %s' % (threading.current_thread().name, n)
#输出当前线程名字 和循环的参数n
print 'thread %s ended.' % threading.current_thread().name
l.release()
# 这里是把线程锁解开,可以再运行写一个线程
print 'thread %s is running...' % threading.current_thread().name
#下面的一部分就是threading的核心用法
#包括target name args 之类的 一般我只用targer=你定义的函数名
t = threading.Thread(target=loop, name='线程名:')
l = threading.Lock()
# 这里申明一个线程锁
t.start()
#开始
t.join()
#关于join的相关信息我会在后面的代码详说
print 'thread %s ended.' % threading.current_thread().name
使用线程锁后,程序按照一个一个有序执行。其中lock还有Rlock的方法,RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。否则会出现死循环,程序不知道解哪一把锁。注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的锁
案例三 join()方法的使用
在多线程中,每个线程自顾执行自己的任务,当最后一个线程运行完毕后再退出,所以这个时候如果你要打印信息的话,会看到打印出来的信息错乱无章,有的时候希望主线程能够等子线程执行完毕后在继续执行,就是用join()方法。
import sys
import threading
import time
reload(sys)
sys.setdefaultencoding('utf-8')
t00 = time.time()
# 获取当前时间戳
def cs1():
time0 = time.time()
for x in range(9):
print x + time.time()-time0
# 计算用了多少时间
print threading.current_thread().name
# 打印这个线程名字
def cs2():
for x1 in range(6,9):
print x1
print threading.current_thread().name
threads=[]
# 定义一个空的列表
t1 = threading.Thread(target=cs1)
t2 = threading.Thread(target=cs2)
threads.append(t1)
threads.append(t2)
# 把这两个线程的任务加载到这个列表中
for x in threads:
x.start()
# 然后执行,这个案例很常用,就是有多个函数要多线程执行的时候用到
# 如果一个程序有多个函数,但是你只想其中的某一个或者某两个函数多线程,用法一样加入空的列表即可
x.join()
#线程堵塞 先运行第一个在运行第二个
#x.join()
#注意你的join放在这里是没有意义的,和不加join一样。线程不堵塞 但是会出现不匀称的表现 并且会修改不同线程中的变量
print 'use time.{}'.format(time.time()-t00)
关于setDaemon()的概念就是:主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起。
案例四 线程锁之信号Semaphore
类名:BoundedSemaphore。这种锁允许一定数量的线程同时更改数据,它不是互斥锁。比如地铁安检,排队人很多,工作人员只允许一定数量的人进入安检区,其它的人继续排队。
import time
import threading
def run(n, se):
se.acquire()
print("run the thread: %s" % n)
time.sleep(1)
se.release()
# 设置允许5个线程同时运行
semaphore = threading.BoundedSemaphore(5)
for i in range(20):
t = threading.Thread(target=run, args=(i,semaphore))
t.start()
运行后,可以看到5个一批的线程被放行。
案例五 线程锁之事件Event
事件线程锁的运行机制:
全局定义了一个Flag,如果Flag的值为False,那么当程序执行wait()方法时就会阻塞,如果Flag值为True,线程不再阻塞。这种锁,类似交通红绿灯(默认是红灯),它属于在红灯的时候一次性阻挡所有线程,在绿灯的时候,一次性放行所有排队中的线程。
事件主要提供了四个方法set()、wait()、clear()和is_set()。
调用clear()方法会将事件的Flag设置为False。
调用set()方法会将Flag设置为True。
调用wait()方法将等待“红绿灯”信号。
is_set():判断当前是否"绿灯放行"状态
下面是一个模拟红绿灯,然后汽车通行的例子:
#利用Event类模拟红绿灯
import threading
import time
event = threading.Event()
# 定义一个事件的对象
def lighter():
green_time = 5
# 绿灯时间
red_time = 5
# 红灯时间
event.set()
# 初始设为绿灯
while True:
print("\33[32;0m 绿灯亮...\033[0m")
time.sleep(green_time)
event.clear()
print("\33[31;0m 红灯亮...\033[0m")
time.sleep(red_time)
event.set()
def run(name):
while True:
if event.is_set():
# 判断当前是否"放行"状态
print("一辆[%s] 呼啸开过..." % name)
time.sleep(1)
else:
print("一辆[%s]开来,看到红灯,无奈的停下了..." % name)
event.wait()
print("[%s] 看到绿灯亮了,瞬间飞起....." % name)
if __name__ == '__main__':
light = threading.Thread(target=lighter,)
light.start()
for name in ['奔驰', '宝马', '奥迪']:
car = threading.Thread(target=run, args=(name,))
car.start()
运行结果:
绿灯亮...
一辆[奔驰] 呼啸开过...
一辆[宝马] 呼啸开过...
一辆[奥迪] 呼啸开过...
一辆[奥迪] 呼啸开过...
......
红灯亮...
一辆[宝马]开来,看到红灯,无奈的停下了...
一辆[奥迪]开来,看到红灯,无奈的停下了...
一辆[奔驰]开来,看到红灯,无奈的停下了...
绿灯亮...
[奥迪] 看到绿灯亮了,瞬间飞起.....
一辆[奥迪] 呼啸开过...
[奔驰] 看到绿灯亮了,瞬间飞起.....
一辆[奔驰] 呼啸开过...
[宝马] 看到绿灯亮了,瞬间飞起.....
一辆[宝马] 呼啸开过...
一辆[奥迪] 呼啸开过...
......
案例六 线程锁之条件Condition
Condition称作条件锁,依然是通过acquire()/release()加锁解锁。
wait([timeout])方法将使线程进入Condition的等待池等待通知,并释放锁。使用前线程必须已获得锁定,否则将抛出异常。
notify()方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池),其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
notifyAll()方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
实际案例
import threading
import time
num = 0
con = threading.Condition()
class Foo(threading.Thread):
def __init__(self, name, action):
super(Foo, self).__init__()
self.name = name
self.action = action
def run(self):
global num
con.acquire()
print("%s开始执行..." % self.name)
while True:
if self.action == "add":
num += 1
elif self.action == 'reduce':
num -= 1
else:
exit(1)
print("num当前为:", num)
time.sleep(1)
if num == 5 or num == 0:
print("暂停执行%s!" % self.name)
con.notify()
con.wait()
print("%s开始执行..." % self.name)
con.release()
if __name__ == '__main__':
a = Foo("线程A", 'add')
b = Foo("线程B", 'reduce')
a.start()
b.start()
如果不强制停止,程序会一直执行下去,并循环下面的结果:
线程A开始执行...
num当前为: 1
num当前为: 2
num当前为: 3
num当前为: 4
num当前为: 5
暂停执行线程A!
线程B开始执行...
num当前为: 4
num当前为: 3
num当前为: 2
num当前为: 1
num当前为: 0
暂停执行线程B!
线程A开始执行...
num当前为: 1
num当前为: 2
num当前为: 3
num当前为: 4
num当前为: 5
暂停执行线程A!
线程B开始执行...
案例 七定时器
定时器Timer类是threading模块中的一个小工具,用于指定n秒后执行某操作。一个简单但很实用的东西。
from threading import Timer
def hello():
print("hello, world")
t = Timer(1, hello)
# 表示1秒后执行hello函数
t.start()
案例八 通过with语句使用线程锁
类似于上下文管理器,所有的线程锁都有一个加锁和释放锁的动作,非常类似文件的打开和关闭。在加锁后,如果线程执行过程中出现异常或者错误,没有正常的释放锁,那么其他的线程会造到致命性的影响。通过with上下文管理器,可以确保锁被正常释放。其格式如下:
with some_lock:
# 执行任务...
这相当于:
some_lock.acquire()
try:
# 执行任务..
finally:
some_lock.release()
threading 的常用属性
current_thread() 返回当前线程
active_count() 返回当前活跃的线程数,1个主线程+n个子线程
get_ident() 返回当前线程
enumerater() 返回当前活动 Thread 对象列表
main_thread() 返回主 Thread 对象
settrace(func) 为所有线程设置一个 trace 函数
setprofile(func) 为所有线程设置一个 profile 函数
stack_size([size]) 返回新创建线程栈大小;或为后续创建的线程设定栈大小为 size
TIMEOUT_MAX Lock.acquire(), RLock.acquire(), Condition.wait() 允许的最大超时时间
线程池 threadingpool
在使用多线程处理任务时也不是线程越多越好。因为在切换线程的时候,需要切换上下文环境,线程很多的时候,依然会造成CPU的大量开销。为解决这个问题,线程池的概念被提出来了。
预先创建好一个数量较为优化的线程组,在需要的时候立刻能够使用,就形成了线程池。在Python中,没有内置的较好的线程池模块,需要自己实现或使用第三方模块。
需要注意的是,线程池的整体构造需要自己精心设计,比如某个函数定义存在多少个线程,某个函数定义什么时候运行这个线程,某个函数定义去获取线程获取任务,某个线程设置线程守护(线程锁之类的),等等…
在网上找了几个案例,供大家学习参考。
下面是一个简单的线程池:
import queue
import time
import threading
class MyThreadPool:
def __init__(self, maxsize=5):
self.maxsize = maxsize
self._pool = queue.Queue(maxsize) # 使用queue队列,创建一个线程池
for _ in range(maxsize):
self._pool.put(threading.Thread)
def get_thread(self):
return self._pool.get()
def add_thread(self):
self._pool.put(threading.Thread)
def run(i, pool):
print('执行任务', i)
time.sleep(1)
pool.add_thread() # 执行完毕后,再向线程池中添加一个线程类
if __name__ == '__main__':
pool = MyThreadPool(5) # 设定线程池中最多只能有5个线程类
for i in range(20):
t = pool.get_thread() # 每个t都是一个线程类
obj = t(target=run, args=(i, pool)) # 这里的obj才是正真的线程对象
obj.start()
print("活动的子线程数: ", threading.active_count()-1)
分析一下上面的代码:
- 实例化一个MyThreadPool的对象,在其内部建立了一个最多包含5个元素的阻塞队列,并一次性将5个Thread类型添加进去。
- 循环100次,每次从pool中获取一个thread类,利用该类,传递参数,实例化线程对象。
- 在run()方法中,每当任务完成后,又为pool添加一个thread类,保持队列中始终有5个thread类。
- 一定要分清楚,代码里各个变量表示的内容。t表示的是一个线程类,也就是threading.Thread,而obj才是正真的线程对象。
上面的例子是把线程类当做元素添加到队列内,从而实现的线程池。这种方法比较糙,每个线程使用后就被抛弃,并且一开始就将线程开到满,因此性能较差。下面是一个相对好一点的例子,在这个例子中,队列里存放的不再是线程类,而是任务,线程池也不是一开始就直接开辟所有线程,而是根据需要,逐步建立,直至池满。
# -*- coding:utf-8 -*-
"""
一个基于thread和queue的线程池,以任务为队列元素,动态创建线程,重复利用线程,
通过close和terminate方法关闭线程池。
"""
import queue
import threading
import contextlib
import time
# 创建空对象,用于停止线程
StopEvent = object()
def callback(status, result):
"""
根据需要进行的回调函数,默认不执行。
:param status: action函数的执行状态
:param result: action函数的返回值
:return:
"""
pass
def action(thread_name, arg):
"""
真实的任务定义在这个函数里
:param thread_name: 执行该方法的线程名
:param arg: 该函数需要的参数
:return:
"""
# 模拟该函数执行了0.1秒
time.sleep(0.1)
print("第%s个任务调用了线程 %s,并打印了这条信息!" % (arg+1, thread_name))
class ThreadPool:
def __init__(self, max_num, max_task_num=None):
"""
初始化线程池
:param max_num: 线程池最大线程数量
:param max_task_num: 任务队列长度
"""
# 如果提供了最大任务数的参数,则将队列的最大元素个数设置为这个值。
if max_task_num:
self.q = queue.Queue(max_task_num)
# 默认队列可接受无限多个的任务
else:
self.q = queue.Queue()
# 设置线程池最多可实例化的线程数
self.max_num = max_num
# 任务取消标识
self.cancel = False
# 任务中断标识
self.terminal = False
# 已实例化的线程列表
self.generate_list = []
# 处于空闲状态的线程列表
self.free_list = []
def put(self, func, args, callback=None):
"""
往任务队列里放入一个任务
:param func: 任务函数
:param args: 任务函数所需参数
:param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数
1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
:return: 如果线程池已经终止,则返回True否则None
"""
# 先判断标识,看看任务是否取消了
if self.cancel:
return
# 如果没有空闲的线程,并且已创建的线程的数量小于预定义的最大线程数,则创建新线程。
if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
self.generate_thread()
# 构造任务参数元组,分别是调用的函数,该函数的参数,回调函数。
w = (func, args, callback,)
# 将任务放入队列
self.q.put(w)
def generate_thread(self):
"""
创建一个线程
"""
# 每个线程都执行call方法
t = threading.Thread(target=self.call)
t.start()
def call(self):
"""
循环去获取任务函数并执行任务函数。在正常情况下,每个线程都保存生存状态, 直到获取线程终止的flag。
"""
# 获取当前线程的名字
current_thread = threading.currentThread().getName()
# 将当前线程的名字加入已实例化的线程列表中
self.generate_list.append(current_thread)
# 从任务队列中获取一个任务
event = self.q.get()
# 让获取的任务不是终止线程的标识对象时
while event != StopEvent:
# 解析任务中封装的三个参数
func, arguments, callback = event
# 抓取异常,防止线程因为异常退出
try:
# 正常执行任务函数
result = func(current_thread, *arguments)
success = True
except Exception as e:
# 当任务执行过程中弹出异常
result = None
success = False
# 如果有指定的回调函数
if callback is not None:
# 执行回调函数,并抓取异常
try:
callback(success, result)
except Exception as e:
pass
# 当某个线程正常执行完一个任务时,先执行worker_state方法
with self.worker_state(self.free_list, current_thread):
# 如果强制关闭线程的flag开启,则传入一个StopEvent元素
if self.terminal:
event = StopEvent
# 否则获取一个正常的任务,并回调worker_state方法的yield语句
else:
# 从这里开始又是一个正常的任务循环
event = self.q.get()
else:
# 一旦发现任务是个终止线程的标识元素,将线程从已创建线程列表中删除
self.generate_list.remove(current_thread)
def close(self):
"""
执行完所有的任务后,让所有线程都停止的方法
"""
# 设置flag
self.cancel = True
# 计算已创建线程列表中线程的个数,
# 然后往任务队列里推送相同数量的终止线程的标识元素
full_size = len(self.generate_list)
while full_size:
self.q.put(StopEvent)
full_size -= 1
def terminate(self):
"""
在任务执行过程中,终止线程,提前退出。
"""
self.terminal = True
# 强制性的停止线程
while self.generate_list:
self.q.put(StopEvent)
# 该装饰器用于上下文管理
@contextlib.contextmanager
def worker_state(self, state_list, worker_thread):
"""
用于记录空闲的线程,或从空闲列表中取出线程处理任务
"""
# 将当前线程,添加到空闲线程列表中
state_list.append(worker_thread)
# 捕获异常
try:
# 在此等待
yield
finally:
# 将线程从空闲列表中移除
state_list.remove(worker_thread)
# 调用方式
if __name__ == '__main__':
# 创建一个最多包含5个线程的线程池
pool = ThreadPool(5)
# 创建100个任务,让线程池进行处理
for i in range(100):
pool.put(action, (i,), callback)
# 等待一定时间,让线程执行任务
time.sleep(3)
print("-" * 50)
print("\033[32;0m任务停止之前线程池中有%s个线程,空闲的线程有%s个!\033[0m"
% (len(pool.generate_list), len(pool.free_list)))
# 正常关闭线程池
pool.close()
print("任务执行完毕,正常退出!")
# 强制关闭线程池
# pool.terminate()
# print("强制停止任务!")
关于线程池其实涉及到工程设计,需要自己很熟练的运行面向对象程序设计。
生产者和消费者模式
生产者就是生成任务,消费者就是解决处理任务。比如在一个程序中,代码是按照重上往下执行,有的时候做等待的时间完全可以用来做任务处理或者做别的事情,为了节省时间,可以借助多线程的功能(自顾自完成自己线程任务)加上Queue队列特性(管道模式。里面存储数据,然后提供给线程处理)完成生产者和消费者模式。关于Queue的用法参考我之前的文章。
案例一
import sys
import Queue
import time
import threading
reload(sys)
sys.setdefaultencoding('utf-8')
q = Queue.Queue(10)
def get(i):
# 这个函数用来生产任务,接受参数i,也可以不传入参数
while 1:
time.sleep(2)
# 这里可以做一些动作,比如过去网站的网址之类的
q.put(i)
# 然后把得到的数据放在消息队列中
def fun(o):
# 这个函数用来处理任务,必须要接受参数
q.get(o)
# 得到获取接受来的参数
print o*10
# 然后对获取的参数作处理,我这里仅仅打印数据乘以10
for i in range(100):
# 生产任务启动,有100个任务量要产生
t1 = threading.Thread(target=get, args=(i,))
t1.start()
for o in range(100):
# 处理任务启动
t = threading.Thread(target=fun, args=(o,))
t.start()
上面这个代码主要是针对骨架进行拆分解说,一般的生产者消费者模式都是这种构架,下面用一个更加清晰的案例来帮助理解。
案例二
# -*- coding:utf-8 -*-
import time
import queue
import threading
q = queue.Queue(10) # 生成一个队列,用来保存“包子”,最大数量为10
def productor(i):
# 厨师不停地每2秒做一个包子
while True:
q.put("厨师 %s 做的包子!" % i)
time.sleep(2)
def consumer(j):
# 顾客不停地每秒吃一个包子
while True:
print("顾客 %s 吃了一个 %s"%(j,q.get()))
time.sleep(1)
# 实例化了3个生产者(厨师)
for i in range(3):
t = threading.Thread(target=productor, args=(i,))
t.start()
# 实例化了10个消费者(顾客)
for j in range(10):
v = threading.Thread(target=consumer, args=(j,))
v.start()
案例三
使用生产者消费者模式实现代理IP扫描并且同步扫描代理IP是否可用,如果不适用生产者消费者模式的话,首先要获取代理IP,然后把获取到的IP放在一个列表,然后在扫描列表的IP,扫描过程为—->获取IP—->IP保存—->IP存活扫描。过程是单向的,也就是说没办法同步一边获取IP然后马上验证。
下面的代码是用生产者消费者模式实现代理IP的获取与存活扫描。
# -*- coding: utf-8 -*-
# @Time : 2018/5/3 0003 10:52
# @Author : Langzi
# @Blog : www.sxadmin.github.io
# @File : 生产者消费者.py
# @Software: PyCharm
import sys
import Queue
import time
import requests
import re
import threading
reload(sys)
sys.setdefaultencoding('utf-8')
q = Queue.Queue(10)
headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}
def get_ip(page):
url1='http://www.66ip.cn/mo.php?sxb=&tqsl=30&port=&export=&ktip=&sxa=&submit=%CC%E1++%C8%A1&textarea='
url2='http://www.xicidaili.com/nn/%s'
for i in range(1,page):
url1_1=url1+str(i)
url2_2=url2+str(i)
try:
r = requests.get(url=url1_1,headers=headers,timeout=5)
#time.sleep(20)
rr = re.findall(' (.*?)<br />',r.content)
for x in rr:
q.put(x)
time.sleep(20)
except Exception,e:
print e
try:
time.sleep(30)
r = requests.get(url=url2_2,headers=headers,timeout=5)
rr = re.findall('/></td>(.*?)<a href',r.content,re.S)
for x in rr:
x1 = x.replace('\n','').replace('<td>','').replace("</td>",':').replace(' ','').replace(': ','')
print x1
q.put(x1)
time.sleep(20)
except Exception,e:
print e
def scan_ip():
while 1:
proxies={}
ip = q.get()
proxies['http'] = str(ip)
try:
req2 = requests.get(url='http://blog.csdn.net/lzy98', proxies=proxies, headers=headers, timeout=5)
if 'One puls' in req2.content:
print str(proxies['http']) + unicode('该代理可正常访问网页...','utf-8')
else:
print unicode(' 该代理无法访问网页,继续验证下一代理...', 'utf-8')
except :
print str(proxies['http'])+unicode(' 无法连接到代理服务器','utf-8')
for i in range(2):
# 这里是要开2个任务量,就是2个线程
t = threading.Thread(target=get_ip,args=(10,))
# 传入的参数是10,回归到get_ip函数,发现传入的参数就是要扫描提供代理网站的页数
t.start()
t1 = threading.Thread(target=scan_ip)
t1.start()
运行结果:
177.132.249.127:20183无法连接到代理服务器
39.104.82.143:8080无法连接到代理服务器
123.231.203.139:8080无法连接到代理服务器
180.250.43.66:8080该代理可正常访问网页...
189.127.238.65:8080无法连接到代理服务器
107.178.3.105:8181该代理可正常访问网页...
95.31.80.67:53281该代理可正常访问网页...
79.174.160.167:8080无法连接到代理服务器
223.242.94.36:31588无法连接到代理服务器
该代理无法访问网页,继续验证下一代理...
5.188.155.243:8080无法连接到代理服务器
180.183.17.151:8080该代理可正常访问网页...
113.90.247.99:8118该代理可正常访问网页...
180.119.65.184:3128无法连接到代理服务器
Python3中的线程池方法
虽然在2版本中并没有线程池,但是在3版本中有相关线程池的使用方法。
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(3)
# 实例化线程池对象,开启3个线程
def fun(a,b):
print (a,b)
returl a**b
# 定义一个函数
executor.submit(fun,2,5) # y运行结果:2,5
# 这是调用与开启线程
result=executor.submit(fun,5,2)
print result # 运行结果: 25
# 如果要有很多参数传入进行运算
executor.map(fun,[1,2,3,4],[2,3,5,6])