线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
查看当前程序线程信息:
import threading #python 中线程的模块有两个
import time
def job():
print('这是一个需要执行的任务')
print('当前所有线程的个数:',threading.active_count()) #查看当前线程的个数
print('当前线程的信息:',threading.current_thread()) #查看当前线程的信息
time.sleep(100)
if __name__ == '__main__':
job()
结果:
线程模块threading
利用threading 模块创建一个线程
import threading
import time
def music(name):
for i in range(2):
print('正在听音乐%s' %(name))
time.sleep(3)
def code(name):
for i in range(2):
print('正在写代码%s' %(name))
time.sleep(3)
if __name__ == '__main__':
start_time = time.time()
p1 = threading.Thread(target=music,args=('haha', ))
p2 = threading.Thread(target=code,args=('linux', ))
p1.start()
p2.start()
p1.join() #join( )方法是对线程进行阻塞
p2.join()
print('花费的时间为:%s' %(time.time()-start_time))
结果:
###线程的阻塞 守护
阻塞:阻塞当前主线程下的所有子线程,待所有子线程执行完毕之后,再执行主线程。
守护:当主线程执行结束之后,所有的子线程被强制结束,不再执行。
阻塞线程主要同过join( )方法实现
守护线程通过setdeamon()方法实现
守护线程:当主线程执行完毕之后,让其余没有执行完 的子线程强制结束
import threading
import time
def music(name):
for i in range(2):
print('正在听音乐%s' %(name))
time.sleep(3)
def code(name):
for i in range(2):
print('正在写代码%s' %(name))
time.sleep(3)
if __name__ == '__main__':
start_time = time.time()
p1 = threading.Thread(target=music,args=('haha', ))
p2 = threading.Thread(target=code,args=('linux', ))
#将p1,p2设置为守护线程,如果将setDaemon中传入True,子线程启动,当主线程执行结束,p1,p2将被强制结束
#设置setDaemon必须在启动线程之前进行设置
p1.setDaemon(True)
p2.setDaemon(True)
p1.start()
p2.start()
print('花费的时间为:%s' %(time.time()-start_time))
结果:
多线程实现主机的连接
import threading
from paramiko.ssh_exception import NoValidConnectionsError,AuthenticationException
import paramiko
def connect(cmd,hostname,port,username,passwd):
#此函数是实现连接主机的功能
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(hostname=hostname,
port=port,
username=username,
password=passwd)
except NoValidConnectionsError as e:
print('%s连接失败' %hostname)
except AuthenticationException as f:
print('%s密码错误' %hostname)
else:
print('%s连接成功' %hostname)
stdin,stdout,stderr = client.exec_command(cmd)
result = stdout.read().decode('utf-8')
print(result)
client.close()
if __name__ == '__main__':
with open('ip.txt') as f: #打开存有主机IP的文件
theads = [] #创建一个线程列表
for i in f:
hostname,port,username,passwd = i.rstrip().split(':') #依次对字符串进行操作,获取连接主机需要的信息
p = threading.Thread(target=connect,args=('uname',hostname,port,username,passwd))
theads.append(p) #将创建的线程对象添加到线程列表中
p.start()
[thread.join() for thread in theads] #依次对线程列表里的每一个列表对象进行阻塞
print('连接任务执行完成')
结果:
多线程获取主机的地理位置
import json
import threading
from urllib.request import urlopen
import time
def job(ip):
url = 'http://ip.taobao.com/service/getIpInfo.php?ip=%s' %(ip)
text = urlopen(url).read().decode('utf-8') #打开url网址的内容,并且解码为utf-8格式
#将获取到的子符串类型转化为字典
d = json.loads(text)['data']
country = d['country']
city = d['city']
print(ip,country,city)
def has_many_thread():
start_time = time.time()
threads = []
ips = ['172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8']
for ip in ips:
p = threading.Thread(target=job,args=(ip, ))
p.start()
threads.append(p)
[thread.join() for thread in threads]
print('使用线程花费的时间为:%s' %(time.time()-start_time))
def has_no_thread():
start_time = time.time()
ips = ['172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8']
for ip in ips:
job(ip)
print('不使用线程花费的时间为:%s' % (time.time() - start_time))
if __name__ == '__main__':
has_many_thread()
has_no_thread()
结果:
通过继承的方法创建线程
import threading
#实质:将实力化对象方法中的新建对象改为新建一个run方法
class IPthread(threading.Thread):
#重新写构造方法
def __init__(self,jobname):
super(IPthread,self).__init__()
self.jobname = jobname
#将多线程执行的任务写入run 方法中
def run(self):
print('this is a job')
t1 = IPthread(jobname='now job')
t1.start()
结果:
线程锁
线程锁的作用:多个线程对同一个数据进行操作时,防止出现不的情况。
例:在下面的代码中,如果不加线程锁,最后的出的money值就不0.只有加了线程锁之后,才会的money值为0的结果
import threading
money = 0
def add(lock):
#对变量进行操作之前进行加所
lock.acquire()
global money
for i in range(10000000):
money += i
#操作完成之后进行解所
lock.release()
def reduce(lock):
lock.acquire()
global money
for i in range(10000000):
money -= i
lock.release()
if __name__ == '__main__':
#创建一个锁对象
lock = threading.Lock()
t1 = threading.Thread(target=add,args=(lock, )) #将出创建的锁对象传入
t2 = threading.Thread(target=reduce,args=(lock, ))
t1.start()
t2.start()
t1.join()
t2.join()
print('最终金额为:%s' %(money))
结果:
全局解释器锁(GUL)
Global Interpreter Lock(GIL)会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常之快,会让你误以为线程是并行执行的,
但是实际上都是轮流执行。经过GIL这一道关卡处理,会增加执行的开销。这意味着,如果你想提高代码的运行速度.
队列
当线程执行的过程中,有返回值时选择队列作为容器来存入和读出数据
#在多线程中,使用队列来返回数据
import threading
from mytimeit import timeit
from queue import Queue
def job(l,queue):
queue.put(sum(l)) #将任务存储到队列中
@timeit
def use_thread():
q = Queue() #创建一个队列对象
li = [[1,2,3,4,5],[2,3,4,5,6],[3,4,5,6,7]]
threads = []
for i in li:
t = threading.Thread(target=job,args=(i,q))
threads.append(t)
t.start()
[thread.join() for thread in threads]
result = [q.get() for _ in li] #将队列中数据转入到列表中
print(result)
if __name__ == "__main__":
use_thread()
结果:
多线程在消费者模型中的运用
下面的代码中主要利用q 队列对象在生产者和消费者之间传送数据。
from asyncio import Queue
from urllib.request import urlopen
import threading
class Prouducer(threading.Thread): #生成可以访问的IP
def __init__(self,q):
super(Prouducer,self).__init__()
self.q = q
def run(self):
with open('IP.txt') as f:
ips = [i.rstrip() for i in f]
ports = [80, 443, 7001, 7002, 8000, 8080, 9000, 9001]
urls = ['http://%s:%s' % (i, j) for i in ips for j in ports]
for url in urls:
self.q.put(url)
class Consmuer(threading.Thread): #访问IP
def __init__(self,q):
super(Consmuer,self).__init__()
self.q = q
def run(self):
try:
url = self.q.get()
urlobj = urlopen(url)
except Exception as e:
print('%s连接失败' %url)
else:
print('%s连接成功' %url)
if __name__ == '__main__':
q = Queue() #创建一个队列对象让其在生产者和消费者之间进行数据的传输
p1 = Prouducer(q)
p1.start()
for i in range(100):
p2 = Consmuer(q)
p2.start()
线程池
注意:只有python3.2以后的版本才支持线程池
from concurrent.futures import ThreadPoolExecutor
import time
def job():
print('this is a job')
return 'haha'
if __name__ == "__main__":
#创建一个线程池对象,设置最大处理线程个数为10
pool = ThreadPoolExecutor(max_workers=10)
#向线程池里面丢所要执行的任务,每丢一个返回一个返回一个对象
f1 = pool.submit(job)
f2 = pool.submit(job)
#判断丢进去的任务是否执行完毕
print(f1.done())
time.sleep(1)
print(f2.done())
#获取任务执行的结果
print(f1.result())
print(f2.result())
结果:
利用线程池连接多台主机
import paramiko
from paramiko.ssh_exception import AuthenticationException,NoValidConnectionsError
from concurrent.futures import ThreadPoolExecutor
def connect(cmd,hostname,port,username,passwd):
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(hostname=hostname,
port=port,
username=username,
password=passwd)
print('正在连接%s主机'.center(50,'*') %hostname)
except AuthenticationException as e:
print('%s密码错误' %hostname)
except NoValidConnectionsError as e:
print('%s登陆失败' %hostname)
else:
stdin,stdout,stderr = client.exec_command(cmd)
result = stdout.read().decode('utf-8')
print(result)
if __name__ == "__main__":
pool = ThreadPoolExecutor() #创建一个线程池对象
with open('ip.txt') as f:
for i in f:
hostname,port,username,passwd = i.rstrip().split(':')
f = pool.submit(connect,'uname',hostname,port,username,passwd) #注意:通过线程池往里面添加任务时,传入的参数格式,与Thread不同
结果:
在线程池中除过submit向线程池里面提交任务之外,还可以通过map方法对,对每一个需要进行操作的数据利用map函数进行操作。
from urllib.error import HTTPError
from urllib.request import urlopen
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import as_completed
import time
URLS = ['http://httpbin.org', 'http://example.com/',
'https://api.github.com/']*20
def get_page(url,timeout=3):
try:
content = urlopen(url).read()
print(content)
return {'url':url,'len':len(content)}
except HTTPError as e:
return {'url':url,'len':0}
start_time = time.time()
pool = ThreadPoolExecutor(max_workers=20)
for res in pool.map(get_page,URLS): #通过map方法对URLS里的每一个ip进行一个get_page操作
print(res)
结果: