一、paramiko模块
1.1 简介
paramiko是一个自由和开放源码模块使用,实现SSH2协议安全(认证和加密)连接到远程计算机,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实。
1.2 paramiko简单使用
1)SSHClient:
基于用户名和密码进行连接
import paramiko #创建ssh对象 ssh = paramiko.SSHClient() #允许连接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #连接服务器 ssh.connect(hostname='192.168.1.108',port=22,username='root',password='123456') #执行命令 stdin,stdout,stderr = ssh.exec_command('df -h') #获取命令结果 result = stdout.read().decode() #打印结果 print(result) #关闭连接 ssh.close()
显示结果:
文件系统 容量 已用 可用 已用%% 挂载点
/dev/sda3 18G 13G 4.3G 75% /
tmpfs 495M 4.0K 495M 1% /dev/shm
/dev/sda1 190M 48M 133M 27% /boot
SSHClient封装Transport:
import paramiko transport = paramiko.Transport(('192.168.1.108',22)) transport.connect(username='root',password='123456') ssh = paramiko.SSHClient() ssh._transport = transport stdin,stdout,stderr = ssh.exec_command('free -m') print(stdout.read().decode()) transport.close()
结果显示:
total used free shared buffers cached
Mem: 988 437 551 0 52 77
-/+ buffers/cache: 307 681
Swap: 2047 0 2047
基于公钥密钥连接:
import paramiko private_key = paramiko.RSAKey.from_private_key_file('C:\id_rsa') #创建ssh对象 ssh = paramiko.SSHClient() #允许连接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #连接服务器 ssh.connect(hostname='192.168.1.108',port=22,username='root',pkey=private_key) #执行命令 stdin,stdout,stderr = ssh.exec_command('df -h') #获取命令结果 result = stdout.read().decode() print(result) #关闭连接 ssh.close()
显示结果:
文件系统 容量 已用 可用 已用%% 挂载点
/dev/sda3 18G 13G 4.3G 75% /
tmpfs 495M 4.0K 495M 1% /dev/shm
/dev/sda1 190M 48M 133M 27% /boot
SSHClient封装transport:
import paramiko private_key = paramiko.RSAKey.from_private_key_file('C:\id_rsa') transport = paramiko.Transport(('192.168.1.108',22)) transport.connect(username='root',pkey=private_key) ssh = paramiko.SSHClient() ssh._transport = transport stdin,stdout,stderr = ssh.exec_command('df -h') print(stdout.read().decode()) transport.close()
2) SFTPClient
用于连接远程服务器并执行上传下载
import paramiko transport = paramiko.Transport(('192.168.1.108',22)) transport.connect(username='root',password='123456') sftp = paramiko.SFTPClient.from_transport(transport) #本地的id_rsa文件上传至服务器/tmp/下面 sftp.put('C:\id_rsa','/tmp/id_rsa') #将/tmp下面的nihao下载到本地的C:目录下 sftp.get('/tmp/nihao','C:\\nihao') transport.close()
基于公钥密钥上传下载
import paramiko private_key = paramiko.RSAKey.from_private_key_file('C:\id_rsa') transport = paramiko.Transport(('192.168.1.108',22)) transport.connect(username='root',pkey=private_key) sftp = paramiko.SFTPClient.from_transport(transport) #本地的id_rsa文件上传至服务器/tmp/下面 sftp.put('C:\id_rsa','/tmp/id_rsa') #将/tmp下面的nihao下载到本地的C:目录下 sftp.get('/tmp/nihao','C:\\nihao') transport.close()
二、线程与进程
2.1 什么是线程?
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
2.2 什么是进程?
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。里面包含对各种资源的调用,内存的管理,网络接口的调用等。。。对各种资源管理的集合 就可以称为 进程
2.3 线程和进程的区别
(1)线程共享内存空间;进程的内存是独立的
(2)同一个进程的线程之间可以直接交流;两个进程想通信,必须通过一个中间代理来实现
(3)创建新线程很简单; 创建新进程需要对其父进程进行一次克隆
(4)一个线程可以控制和操作同一进程里的其他线程;但是进程只能操作子进程
(5)改变主线程(如优先权),可能会影响其它线程;改变父进程,不影响子进程
2.4 Python GIL(Global Interpreter Lock)
无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行,这还叫什么多线程呀?
需要明确的一点是GIL
并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL
归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL 。
三、threading模块
3.1 直接调用:
import threading import time def func(name): print("hello",name) time.sleep(3) t1 = threading.Thread(target=func,args=("zj",)) t2 = threading.Thread(target=func,args=("csp",)) #并发 t1.start() #并发 t2.start()
3.2 集成式调用
import threading,time class MyThread(threading.Thread): def __init__(self,num): threading.Thread.__init__(self) self.num = num #定义每个线程要运行的函数 def run(self): print("running on number:%s" %self.num) time.sleep(3) if __name__ == '__main__': t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start()
显示结果:
running on number:1
running on number:2
四、join与daemon
4.1 join():主程序(主线程)会等待其他线程执行完。
join ()方法:主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。
原型:join([timeout])
里面的参数时可选的,代表线程运行的最大时间,即如果超过这个时间,不管这个此线程有没有执行完毕都会被回收,然后主线程或函数都会接着执行的。
import threading,time def func(name): print("i am",name) time.sleep(3) print("this thread done...") start_time = time.time() #这里主线程和主线程启动的50个线程均为并行,互不影响,算上一个主线程相当于51个线程并发。 res = [] for i in range(50): t = threading.Thread(target=func,args=(i,)) t.start() res.append(t) for j in res: j.join() print ("all thread has finished...",threading.current_thread()) print("total time:",time.time() - start_time)
4.2 daemon:设置守护线程。程序会等待【非守护线程】结束才退出,不会等【守护线程】
setDaemon()方法。主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起。
例子:就是设置子线程随主线程的结束而结束
import time import threading def run(n): print('[%s]----running---\n' % n) time.sleep(2) print('----done----') def main(): for i in range(5): t = threading.Thread(target=run,args=[i,]) t.start() t.join(1) print('starting thread',t.getName()) m = threading.Thread(target=main,args=[]) #将main线程设置为Daemon线程,它作为程序主线程的守护的守护线程,当主线程退出时,m线程也会退出,由m启动的其他子线程会同时退出,不管是否执行完任务 m.setDaemon(True) m.start() m.join(timeout=2) print("---main thread done---")
五、线程锁、互斥锁
5.1 线程锁
CPU执行任务时,在线程之间是进行随机调度的,并且每个线程可能只执行n条代码后就转而执行另外一条线程。由于在一个进程中的多个线程之间是共享资源和数据的,这就容易造成资源抢夺或脏数据,于是就有了锁的概念,限制某一时刻只有一个线程能访问某个指定的数据。
import threading,time def addNum(): #在每个线程中都获取这个全局变量 global num print('---get num:',num) time.sleep(1) #对此公共变量进行-1操作 num -= 1 #设定一个共享变量 num = 100 thread_list = [] for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) #等待所有线程执行完毕 for t in thread_list: t.join() print('final num',num)
正常来讲,这个num结果应该是0, 但是偶尔最后打印出来的num结果不总是0,为什么每次运行的结果不一样呢?假设你有A,B两个线程,此时都 要对num 进行减1操作, 由于2个线程是并发同时运行的,所以2个线程很有可能同时拿走了num=100这个初始变量交给cpu去运算,当A线程去处完的结果是99,但此时B线程运算完的结果也是99,两个线程同时CPU运算的结果再赋值给num变量后,结果就都是99。那怎么办呢? 很简单,每个线程在要修改公共数据时,为了避免自己在还没改完的时候别人也来修改此数据,可以给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。
加线程锁:
import time,threading def addNum(): #在每个线程中都获取这个全局变量 global num print ('---get num:',num) time.sleep(1) #修改数据前加锁 lock.acquire() #对此公共变量进行-1操作 num -= 1 #修改后释放 lock.release() #设定一个共享变量 num = 100 thread_list = [] #生成全局锁 lock = threading.Lock() for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) #等待所有线程执行完毕 for t in thread_list: t.join() print('final num:',num)
递归锁:
大锁里面包含子锁
import threading,time def run1(): print("grab the first part data") lock.acquire() global num num +=1 lock.release() return num def run2(): print("grab the second part data") lock.acquire() global num2 num2+=1 lock.release() return num2 def run3(): lock.acquire() res = run1() print('--------between run1 and run2-----') res2 = run2() lock.release() print(res,res2) if __name__ == '__main__': num,num2 = 0,0 lock = threading.RLock() for i in range(10): t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: print(threading.active_count()) else: print('----all threads done---') print(num,num2)
Semaphore(信号量)
互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
import threading,time def run(n): semaphore.acquire() time.sleep(1) print("run the thread:%s\n" %n) semaphore.release() if __name__ == '__main__': num = 0 #最多允许5个线程同时运行 semaphore = threading.BoundedSemaphore(5) for i in range(20): t = threading.Thread(target=run,args=(i,)) t.start() while threading.active_count() != 1: pass else: print ('---all threads done---') print(num)
显示结果:
run the thread:2
run the thread:4
run the thread:0
run the thread:1
run the thread:3
run the thread:7 run the thread:6
run the thread:5
run the thread:9 run the thread:8
run the thread:11 run the thread:12 run the thread:10
run the thread:13
run the thread:14
run the thread:17 run the thread:15 run the thread:18
run the thread:19
run the thread:16
---all threads done--- 0
六、事件(Event)
事件主要提供了三个方法 set、wait、clear。
事件机制:全局定义了一个“Flag”,如果“Flag”的值为False,那么当程序执行wait方法时就会阻塞,如果“Flag”值为True,那么wait方法时便不再阻塞。
通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
import time,threading,random event = threading.Event() def lighter(): count = 0 #先设置成绿灯 event.set() while True: if count > 5 and count < 10: #改成红灯,把标志位清除 event.clear() print("\033[41;1m red......\033[0m") elif count > 10: #设置成路灯 event.set() count = 0 else: print ("\033[42;1m green \033[0m") time.sleep(1) count += 1 def car(name): while True: #代表绿灯 if event.is_set(): print ("[%s] is running" % name) time.sleep(2) else: print("[%s] is waiting......" % name) event.wait() print("[%s] green light is on,start going" % name) light = threading.Thread(target=lighter,) light.start() car1 = threading.Thread(target=car,args=("Tesla",)) car1.start()
这里还有一个event使用的例子,员工进公司门要刷卡, 我们这里设置一个线程是“门”, 再设置几个线程为“员工”,员工看到门没打开,就刷卡,刷完卡,门开了,员工就可以通过。
import threading import time import random def door(): door_open_time_counter = 0 while True: if door_swiping_event.is_set(): print("\033[32;1mdoor opening....\033[0m") door_open_time_counter +=1 else: print("\033[31;1mdoor closed...., swipe to open.\033[0m") door_open_time_counter = 0 #清空计时器 door_swiping_event.wait() if door_open_time_counter > 3:#门开了已经3s了,该关了 door_swiping_event.clear() time.sleep(0.5) def staff(n): print("staff [%s] is comming..." % n ) while True: if door_swiping_event.is_set(): print("\033[34;1mdoor is opened, passing.....\033[0m") break else: print("staff [%s] sees door got closed, swipping the card....." % n) print(door_swiping_event.set()) door_swiping_event.set() print("after set ",door_swiping_event.set()) time.sleep(0.5) door_swiping_event = threading.Event() #设置事件 door_thread = threading.Thread(target=door) door_thread.start() for i in range(5): p = threading.Thread(target=staff,args=(i,)) time.sleep(random.randrange(3)) p.start()
七、queue队列
队列是一种先进先出的数据结构,与之对应的是堆栈这种后进先出的结构。但是在python中,它内置了一个queue模块,它不但提供普通的队列,还提供一些特殊的队列。
先进先出队列:
import queue q = queue.Queue(5) q.put(11) q.put(22) q.put(33) print(q.get()) print(q.get()) print(q.get())
显示结果:
11
22
33
Queue类的参数和方法:
-
qsize() 获取当前队列中元素的个数,也就是队列的大小
-
empty() 判断当前队列是否为空,返回True或者False
-
full() 判断当前队列是否已满,返回True或者False
-
put(self, block=True, timeout=None)
- get(self, block=True, timeout=None)
LifoQueue:后进先出队列
import queue q = queue.LifoQueue() q.put(1) q.put(2) print(q.get()) print(q.get())
显示结果:
2
1
PriorityQueue:优先级队列
import queue q = queue.PriorityQueue() q.put((10,"haihao")) q.put((-1,"nihao")) q.put((6,"buhao")) print(q.get()) print(q.get()) print(q.get())
显示结果:
(-1, 'nihao')
(6, 'buhao')
(10, 'haihao')
八、产者消费者模型
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
下面来学习一个最基本的生产者消费者模型的例子
import threading import queue def producer(): for i in range(10): q.put("骨头 %s" % i ) print("开始等待所有的骨头被取走...") q.join() print("所有的骨头被取完了...") def consumer(n): while q.qsize() >0: print("%s 取到" %n , q.get()) q.task_done() #告知这个任务执行完了 q = queue.Queue() p = threading.Thread(target=producer,) p.start() c1 = consumer("帅锅")
包子案例:
import time,random import queue,threading q = queue.Queue() def Producer(name): count = 0 while count <20: time.sleep(random.randrange(3)) q.put(count) print('Producer %s has produced %s baozi..' %(name, count)) count +=1 def Consumer(name): count = 0 while count <20: time.sleep(random.randrange(4)) if not q.empty(): data = q.get() print(data) print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data)) else: print("-----no baozi anymore----") count +=1 p1 = threading.Thread(target=Producer, args=('A',)) c1 = threading.Thread(target=Consumer, args=('B',)) p1.start() c1.start()