一、paramiko
二、进程、与线程区别
三、python GIL全局解释器锁
四、线程
- 语法
- join
- 线程锁之LockRlock信号量
- 将线程变为守护进程
- Event事件
- queue队列
- 生产者消费者模型
一、paramiko
用于远程连接并执行简单的命令
使用用户名密码连接:
1 import paramiko 2 3 # 创建SSH对象 4 ssh = paramiko.SSHClient() 5 # 允许连接不在know_hosts文件中的主机 6 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 7 # 连接服务器 8 ssh.connect(hostname='172.16.5.163', port=22, username='root', password='111111') 9 10 # 执行命令 11 stdin, stdout, stderr = ssh.exec_command('df') 12 # 获取命令结果 13 result = stdout.read() 14 print(result.decode()) 15 16 # 关闭连接 17 ssh.close() 18 19 20 结果 21 Filesystem 1K-blocks Used Available Use% Mounted on 22 /dev/mapper/VolGroup-lv_root 51606140 1518048 47466652 4% / 23 tmpfs 510172 0 510172 0% /dev/shm 24 /dev/sda1 495844 33461 436783 8% /boot 25 /dev/mapper/VolGroup-lv_home 2059640248 203016 1954813516 1% /home
使用公钥连接
import paramiko private_key = paramiko.RSAKey.from_private_key_file('id_rsa.txt') #创建SSH对象 ssh = paramiko.SSHClient() #允许连接不在know_host文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #连接服务器 ssh.connect(hostname='172.16.5.163',port=22,username='root',pkey=private_key) #执行命令 stdin,stdout,stderr = ssh.exec_command('df -h') #获取命令结果 restult = stdout.read() #打印执行结果 print(restult.decode()) #关闭连接 ssh.close()
SFTPClient使用用户名密码完成上传下载
1 import paramiko 2 3 transport = paramiko.Transport(('172.16.5.163',22)) 4 transport.connect(username='root',password='111111') 5 6 sftp = paramiko.SFTPClient.from_transport(transport) 7 # 将location.py 上传至服务器 /tmp/test.py 8 sftp.put('D:\test1\put.txt', '/tmp/put.txt') 9 # 将remove_path 下载到本地 local_path 10 sftp.get('/tmp/get.txt', 'D:\test1\get.txt') 11 12 transport.close()
SFTPClient使用公钥完成上传下载
1 import paramiko 2 3 private_key = paramiko.RSAKey.from_private_key_file('id_rsa.txt') 4 5 transport = paramiko.Transport(('172.16.5.163', 22)) 6 transport.connect(username='root', pkey=private_key ) 7 8 sftp = paramiko.SFTPClient.from_transport(transport) 9 # 将location.py 上传至服务器 /tmp/test.py 10 sftp.put('D:\test1\put.txt', '/tmp/put.txt') 11 # 将remove_path 下载到本地 local_path 12 sftp.get('/tmp/get.txt', 'D:\test1\get.txt') 13 14 transport.close()
二、进程、与线程区别
线程:是操作系统的最小调度单元,一堆指令的集合。
进程:操作CPU,必须先创建一个线程
进程和线程的区别 启动一个线程比启动一个进程快,运行速度没有可比性。 先有一个进程然后才能有线程。 1、进程包含线程 2、线程共享内存空间 3、进程内存是独立的(不可互相访问) 4、进程可以生成子进程,子进程之间互相不能互相访问(相当于在父级进程克隆两个子进程) 5、在一个进程里面线程之间可以交流。两个进程想通信,必须通过一个中间代理来实现 6、创建新线程很简单,创建新进程需要对其父进程进行克隆。 7、一个线程可以控制或操作同一个进程里面的其它线程。但进程只能操作子进程。 8、父进程可以修改不影响子进程,但不能修改。
三、python GIL全局解释器锁
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
上面的核心意思就是,无论你启多少个线程,你有多少个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
详细说明:http://www.dabeaz.com/python/UnderstandingGIL.pdf
其实就是为了解决同一时间内多个线程处理一个运算(浪费资源),全局解释器锁解决同一时间如果有线程执行过了,其它线程就不重复运算。
四、线程
- 语法
写法一:
1 import threading 2 3 #创建一个函数,每个线程要使用的函数 4 def fun(n): 5 print(n) 6 7 n1 = threading.Thread(target=fun,args=('n1',))#生成一个线程 8 n2 = threading.Thread(target=fun,args=('n2',))#再生成一个线程 9 10 n1.start()#启动n1这个线程 11 n2.start()#启动n2这个线程 12 13 print(n1.getName())#获取线程名 14 print(n2.getName())#获取线程名
写法二:
import threading,time class mythread(threading.Thread):#继承thread.Thread这个函数 def __init__(self,n): super(mythread,self).__init__() self.n = n def run(self):#这里这个方法名必须命名为run,否则失败 print('运行线程',self.n) n1 = mythread("n1") n2 = mythread("n2") n1.start() n2.start()
2、join
用法例如线程n1和n2,想要等n1的结果在执行n2,就需要n1.join。具体如下
1 import threading,time 2 3 class mythread(threading.Thread):#继承thread.Thread这个函数 4 def __init__(self,n,sleep_time): 5 super(mythread,self).__init__() 6 self.n = n 7 self.sleep_time = sleep_time 8 9 10 def run(self):#这里这个方法名必须命名为run,否则失败 11 print("开始运行线程",self.n) 12 time.sleep(self.sleep_time)#执行完一个线程后停止2秒 13 print('运行线程结束',self.n,threading.current_thread()) 14 15 16 n1 = mythread("n1",2) 17 n2 = mythread("n2",4) 18 19 n1.start() 20 n2.start() 21 n1.join 22 print('这里是主程序',threading.current_thread())
1 开始运行线程 n1 2 开始运行线程 n2 3 这里是主程序 <_MainThread(MainThread, started 12624)>#一个进程启动收首先会打印主线程 4 运行线程结束 n1 <mythread(Thread-1, started 4020)>#这里是普通线程 5 运行线程结束 n2 <mythread(Thread-2, started 9088)>#这里是普通线程
3、线程锁之LockRlock信号量
线程锁:保证同一时间内只有一个线程可以修改一个数据(避免同一时间内多个线程修改同一个数据)
4、将线程变为守护进程
1 import threading 2 import time 3 4 def run(n): 5 print("task",n) 6 time.sleep(2) 7 print('停') 8 9 10 11 start_time = time.time() 12 t_obj = [] 13 for i in range(50): 14 t = threading.Thread(target=run,args=("t--%s"%i,)) 15 t.setDaemon(True)#把当前线程线程设置为守护进程,必须要写在start前面,否则报错 16 t.start() 17 t_obj.append(t) 18 19 20 print("主线程执行")#主线程 21 print("cost:",time.time() - start_time) 22 23 #非守护进程退出了,就全部退出(这里是主线程执行完毕后,就不等子线程了,主线程退出了,就都退出了)
线程锁(互斥锁Mutex)
一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况?
1 import time 2 import threading 3 4 def addNum(): 5 global num #在每个线程中都获取这个全局变量 6 print('--get num:',num ) 7 time.sleep(1) 8 num -=1 #对此公共变量进行-1操作 9 10 num = 100 #设定一个共享变量 11 thread_list = [] 12 for i in range(100): 13 t = threading.Thread(target=addNum) 14 t.start() 15 thread_list.append(t) 16 17 for t in thread_list: #等待所有线程执行完毕 18 t.join() 19 20 21 print('final num:', num )
正常来讲,这个num结果应该是0, 但在python 2.7上多运行几次,会发现,最后打印出来的num结果不总是0,为什么每次运行的结果不一样呢? 哈,很简单,假设你有A,B两个线程,此时都 要对num 进行减1操作, 由于2个线程是并发同时运行的,所以2个线程很有可能同时拿走了num=100这个初始变量交给cpu去运算,当A线程去处完的结果是99,但此时B线程运算完的结果也是99,两个线程同时CPU运算的结果再赋值给num变量后,结果就都是99。那怎么办呢? 很简单,每个线程在要修改公共数据时,为了避免自己在还没改完的时候别人也来修改此数据,可以给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。
*注:不要在3.x上运行,不知为什么,3.x上的结果总是正确的,可能是自动加了锁
加锁版本
import time import threading def addNum(): global num #在每个线程中都获取这个全局变量 print('--get num:',num ) time.sleep(1) lock.acquire() #修改数据前加锁 num -=1 #对此公共变量进行-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 )
GIL VS Lock
机智的同学可能会问到这个问题,就是既然你之前说过了,Python已经有一个GIL来保证同一时间只能有一个线程来执行了,为什么这里还需要lock? 注意啦,这里的lock是用户级的lock,跟那个GIL没关系 ,具体我们通过下图来看一下+配合我现场讲给大家,就明白了。
RLock(递归锁)
一把大锁中还有很多小锁
1 import threading,time 2 3 def run1(): 4 print("grab the first part data") 5 lock.acquire() 6 global num 7 num +=1 8 lock.release() 9 return num 10 def run2(): 11 print("grab the second part data") 12 lock.acquire() 13 global num2 14 num2+=1 15 lock.release() 16 return num2 17 def run3(): 18 lock.acquire() 19 res = run1() 20 print('--------between run1 and run2-----') 21 res2 = run2() 22 lock.release() 23 print(res,res2) 24 25 26 if __name__ == '__main__': 27 28 num,num2 = 0,0 29 lock = threading.RLock() 30 for i in range(10): 31 t = threading.Thread(target=run3) 32 t.start() 33 34 while threading.active_count() != 1: 35 print(threading.active_count()) 36 else: 37 print('----all threads done---') 38 print(num,num2)
信号量:同一时间内,最大允许多个线程运行
1 import threading,time 2 3 def run(n): 4 semaphore.acquire() 5 time.sleep(1) 6 print("run the thread: %s " %n) 7 semaphore.release() 8 9 10 semaphore = threading.BoundedSemaphore(5) #最多允许5个线程同时运行 11 for i in range(19): 12 t = threading.Thread(target=run,args=(i,)) 13 t.start() 14 15 while threading.active_count() != 1: 16 pass #print threading.active_count() 17 else: 18 print('----all threads done---')
5、Event事件
通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
#event.wait()等待
#event.clear() #把标志位清空
#event.is_set()设定标志位
1 import threading,time 2 event = threading.Event() 3 4 def lighter(): 5 count = 0 6 event.set() #先设定绿灯 7 while True: 8 if count >5 and count < 10:#改为红灯 9 event.clear() #把标志位清空 10 print("