Python成长之路 paramiko与线程
python3的paramiko模块
介绍
paramiko是一个用于远程控制的模块,使用该模块对远程服务器进行命令或者文件操作
模块的安装
paramiko模块我们可以通过pip3 install paramiko的方式来安装,不过我是用的是编译器pycharm内置的功能来安装这个模块的,有时候安装有可能出现错误,不过强大的同行会告诉你如何解决和安装的,这里我就不说了
paramiko模块的使用
远程连接分为两种:
- 基于用户名和密码的连接
- 基于公钥私钥的连接
使用paramiko远程操作的方式有两种
- 使用SSHClient
- 自己创建一个 transport(通常使用它来做sftp)
SSH基于用户名和密码的连接
#!/usr/bin/env python #-*-coding:utf-8-*- import paramiko # 实例化SSH(创建SSH对象) ssh = paramiko.SSHClient() # 允许连接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 连接服务器 ssh.connect(hostname='192.168.132.66', port=22, username='yang', password='redhat') # 执行命令 stdin, stdout, stderr = ssh.exec_command("ls") # 获取命令的结果 result = stdout.read().decode("utf-8") # 接收的结果是bytes类型 err = stderr.read().decode("utf-8") # 判断stderr输出是否为空,为空则打印执行结果,不为空打印报错信息 if not err: print("result:", result) else: print("err", err) ssh.close()
使用paramiko模块中的transport创建sftp
#!/usr/bin/env python #-*-coding:utf-8-*- import paramiko # 实例化SSH(创建SSH对象) transport = paramiko.Transport('192.168.1132.66', 22) transport.connect(username='yang', password='redhat') sftp = paramiko.SFTPClient.from_transport(transport) # 将F:\python\aaa.txt 上传至服务器 /tmp目录下并命名为test.py sftp.put(r'F:\python\aaa.txt', '/tmp/test.txt') # 将'/tmp/qw.txt'下载到本地 F:\python目录下并命名test.txt sftp.get('/tmp/qw.txt', r'F:\python\test.txt') transport.close()
SSH使用秘钥登录
#!/usr/bin/env python #-*-coding:utf-8-*- import paramiko private_key = paramiko.RSAKey.from_private_key_file('id_rsa31.txt') # # 创建SSH对象 ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 连接服务器 ssh.connect(hostname='192.168.132.88', port=22, username='rhce', pkey=private_key) stdin, stdout, stderr = ssh.exec_command('df') res_out = stdout.read() print(res_out.decode()) ssh.close()
关于上面的秘钥问题我们先要生成一对公钥和私钥,把公钥放到我们要登录的服务器的指定目录上,上面的id_rsa31.txt就是私钥。关于公钥和私钥是怎么生成的、放到什么位置这个可以百度一下上面会告诉你怎么做的,这里我就不详细作说了。
sftp使用秘钥登录
#!/usr/bin/env python #-*-coding:utf-8-*- import paramiko # 实例化SSH(创建SSH对象) private_key = paramiko.RSAKey.from_private_key('id_rsa31.txt') transport = paramiko.Transport('192.168.1132.66', 22) transport.connect(username='yang', pkey=private_key) sftp = paramiko.SFTPClient.from_transport(transport) # 将F:\python\aaa.txt 上传至服务器 /tmp目录下并命名为test.py sftp.put(r'F:\python\aaa.txt', '/tmp/test.txt') # 将'/tmp/qw.txt'下载到本地 F:\python目录下并命名test.txt sftp.get('/tmp/qw.txt', r'F:\python\test.txt') transport.close()
上面我只是简单的说了下关于paramiko的远程连接和sftp的实现,要是看的不是很明白的的可以自己在网上搜一搜哪里不懂,对照这看看就行了,这个个人感觉不是很难。难点就是秘钥哪里比较难点,主要是以前没有了解过秘钥登录方面所以我自己感觉相比较其它的是比较难的。如果你用过秘钥的话那就不是那么难了,其它的都还好。
线程
再说线程之前我们说说什么叫做进程。
程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。
在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的
然而线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。我们可以认为进程是资源的集合,而线程是操作系统最小的调度单位。线程与进程运行的速度是没法比较的,因为进程本身不能够运行,是通过操作线程来实现的,所以有可比性。
在python3中实现多线程支持的是threading模块,使用这个模块可以创建多个线程程序,并且在多线程之间进行同步和通讯。
python3中创建线程的两个方法:
- 通过threading.Thread直接在行程中运行函数
- 通过继承threading.Tread类来创建线程
方法一:直接通过threading.Tread
#!/usr/bin/env python # -*-coding-*- import threading import time def run(test): print('This is {}'.format(test)) time.sleep(2) if __name__ == "__main__": t1 = threading.Thread(target=run, args=("test1" ,)) # 创建一个t1线程,用来执行run() t2 = threading.Thread(target=run, args=("test2" ,)) # 创建一个t2线程,用来执行run() t1.start() # 运行t1线程 t2.start() # 运行t2线程 print('所有而测试完毕')
方法二:通过继承threading.Thread类来创建线程
#!/usr/bin/env python # -*-coding-*- import threading import time class Mythread(threading.Thread): def __init__(self,name): super(Mythread, self).__init__() self.name = name def run(self): print("This is {}".format(self.name)) time.sleep(2) if __name__ == "__main__": t1 = Mythread("test1") t2 = Mythread("test2") t1.start() t2.start() print("所有测试运行完毕")
下面我们来看一个例子
#!/usr/bin/env python # -*-coding-*- import threading, time def run(s): print("test {}".format(s)) time.sleep(2) print("----done----") if __name__ == "__main__": start_time = time.time() for i in range(10): t = threading.Thread(target=run, args=(i,)) t.start() print("time", time.time() - start_time)
# 运行结果 test 0 test 1 test 2 test 3 test 4 test 5 test 6 test 7 test 8 test 9 time 0.0020012855529785156 ----done---- ----done---- ----done---- ----done---- ----done---- ----done---- ----done---- ----done---- ----done---- ----done----
从上面的看出主线程结束时子线程还没有结束,在主线程启动了子线程之后,子线程和主线程没有什么关系它们相互独立的,是并行的。
现实中有时候必须要等某一个线程结束了在开始其它的线程,所以在这里我们引进了join()的方法。
join()方法
join()方法是阻塞程序知道某一线程执行完毕才开始继续的执行程序。
#!/usr/bin/env python # -*-coding-*- import threading, time def run(s): print("test {}".format(s)) time.sleep(2) print("----done----") if __name__ == "__main__": t1 = threading.Thread(target=run, args=('test1',)) t2 = threading.Thread(target=run, args=("test2",)) t1.start() # t1.join() t2.start() print("测试完毕") 结果为 test test1 test test2 测试完毕 ----done---- ----done---- 当我们添加t1.join()时结果为 test test1 ----done---- test test2 测试完毕 ----done---- 上面可以看出当我们添加join()方法时只有等t1线程执行结束时才会继续执行下面的程序。
#!/usr/bin/env python # -*-coding-*- import threading, time def run(s, t): print("test {}".format(s)) time.sleep(t) print("----done----") if __name__ == "__main__": t1 = threading.Thread(target=run, args=('test1', 2)) t2 = threading.Thread(target=run, args=("test2", 4)) t1.start() t2.start() t1.join() print("测试完毕") #结果为 test test1 test test2 ----done---- 测试完毕 ----done----
上面2个例子我们可以知道当我们在某个位置为某个线程使用join()时程序走到join()时程序就会阻塞,只有等join()的那个线程结束时,才回执行join()下面的程序。不会等其它的线程结束,所以要想等其它的线程结束那么就要给其它的线程也使用join()方法。
有的时候在一个循环里面启动线程,又要等所有的线程执行结束那我们可以利用
#!/usr/bin/env python # -*-coding-*- import threading, time def run(s): print("test {}".format(s)) time.sleep(2) print("----done----") if __name__ == "__main__": s = [] for i in range(10): t = threading.Thread(target=run, args=("test {}".format(i),)) t.start() s.append(t) for n in s: n.join() print("测试完毕")
上面我们可以在实验中发现我们的主线程结束后子线程如果没有结束那么程序会等到子线程结束后,程序才会结束。如果我们把子线程变为主线程的守护线程的话,那么不过子线程有没有结束当我们的主线程结束时,程序就会结束。
#!/usr/bin/env python # -*-coding-*- import threading, time def run(s): print("test {}".format(s)) time.sleep(2) print("----done----") if __name__ == "__main__": s = [] for i in range(10): t = threading.Thread(target=run, args=("test {}".format(i),)) t.setDaemon(True) # 把子线程变为主线程的守护线程
#设置为守护进程一定要在start之前 t.start() s.append(t) print("测试完毕")
线程锁
当一个进程拥有多个线程之后,如果他们各做各的任务互没有关系还行,但既然属于同一个进程,他们之间总是具有一定关系的。比如多个线程都要对某个数据进行修改,则可能会出现不可预料的结果。为保证操作正确,就需要引入锁来进行线程间的同步。
python3中给出了threading模块中的RLock锁(可以叫做递归锁下面是一个例子)
#!/usr/bin/env python # -*-coding:utf-8-*- import threading import time class Mythread(threading.Thread): def run(self): global num # 声明一个全局变量 lock.acquire() # 上锁,acquire()和release()之间的语句一次只能有一个线程进入,其余线程在acquire()处等待 num += 5 print('%s:%d' % (self.name, num)) lock.release() # 解锁 if __name__ == '__main__': num = 0 lock = threading.RLock() # 创建 可重入锁 l = [] for i in range(5): t = Mythread() l.append(t) # 创建 5 个线程,并把他们放到一个列表中 for i in l: i.start() # 开启列表中的所有线程
semaphore(信号量)
#!/usr/bin/env python # -*-coding:utf-8-*- import threading import time def run(n): semaphore.acquire() time.sleep(1) print("This is {}".format(n)) semaphore.release() if __name__ == "__main__": semaphore = threading.BoundedSemaphore(5) # 最多一次同时运行5个线程 for i in range(30): t = threading.Thread(target=run, args=(i,)) t.start() while threading.active_count() != 1: pass else: print("运行完毕") 这个执行的时候你会看到五个打印运行run()的结果这个表示一次性最多运行五个线程
isAlive()方法
这个是用于当前的线程有没有在运行,有的话返回True没有的话返回Flash
#!/usr/bin/env python # -*-coding:utf-8-*- import threading import time def run(): time.sleep(1) t = threading.Thread(target=run) print(t.isAlive()) # 结果为Flash 因为线程t还没开始 t.start() print(t.isAlive()) # 结果为True 因为线程t正在运行 time.sleep(3) print(t.isAlive()) # 结果为Flash 因为线程t结束运行
现成的同步(Event)
通过Event来实现两个或多个线程之间的交互。
下面是一个红绿灯的例子
import threading,time import random def light(): if not event.isSet(): event.set() count = 0 while True: if count > 8 and count < 13: if event.isSet(): event.clear() # 修改标志清空变为红灯 print("\033[41;1m**红灯**\033[0m") else: print("\033[42;1m**绿灯**\033[0m") if count > 12: count = 0 event.set() # 变为绿灯 time.sleep(1) count += 1 def car(): while 1: time.sleep(random.randrange(10)) # 随机多少秒来一辆车 if event.isSet(): print("汽车开始启动通过马路") else: print("汽车开始等待红灯") if __name__ == "__main__": event = threading.Event() Light = threading.Thread(target=light) Light.start() Car = threading.Thread(target=car) Car.start()
queue队列
在这里我只是简单的说一下队列的使用的分类。
队列的种类:
- 先进先出 (queue.Queue())
- 后进先出 (queue.LifoQueue())
- 根据优先级判断谁先出来 (queue.PriorityQueue())
queue的一些常用的方法:
empty :如果队列为空则返回True
full:如果队列满了,返回True
put : 方静一个元素到队列中
put_nowait:立即放入一个元素,不等待
get:取出一个元素从队列中
get_nowait: 立即取出一个元素,不等待
join: 阻塞调用线程,直到队列中的所有任务被处理掉
qsize: 返回队列中元素的个数
task_done : 在完成一项任务之后,向任务以完成的队列发送一个信号。
下面是关于队列的简单示例:
import queue q = queue.Queue(maxsize=10) # 先进先出 print(q.empty()) # 判断队列是否为空 for i in range(10): q.put("第{}位进来".format(i)) print(q.full()) # 判断队列是否满了 for i in range(20): print(q.get()) if q.empty(): break q = queue.LifoQueue(maxsize=10) # 后进先出 print(q.empty()) # 判断队列是否为空 for i in range(10): q.put("第{}位进来".format(i)) print(q.full()) # 判断队列是否满了 for i in range(20): print(q.get()) if q.empty(): break q = queue.PriorityQueue() # 优先级 q.put((1, 'qqq')) q.put((7, 'www')) q.put((3, 'eee')) for i in range(q.qsize()): print(q.get())
消费者生产者模型
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
下面的例子基于队列实现生产者消费者模型,实现的做包子和吃包子的事情
#!/usr/bi n/env python # -*-coding:utf-8-*- import threading,queue,time q = queue.Queue(maxsize=10) def producer(content): i = 1 while True: q.put("面点师傅做了{}馅的包子".format(content)) print("做了{}包子".format(i)) time.sleep(3) i += 1 def consumer(name): while q.qsize() >= 0: print("{}拿了{}馅的包子吃了".format(name, q.get())) time.sleep(1) p = threading.Thread(target=producer, args=('猪肉',)) p.start() c1 = threading.Thread(target=consumer, args=("小张",)) c2 = threading.Thread(target=consumer, args=("小李",)) c1.start() c2.start()