多线程

线程

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
查看当前程序线程信息:

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)

结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值