Python学习笔记之多线程
Ftp
# ftp 文件传输协议 FTP(File Transfer Protocol) 文件传输协议 的英文简称,而中文简称为"文传协议"。用于internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。 基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在FTP的使用当中,用户经常遇到两个慨念:下载(Download)和上传(Upload)。 "下载"文件就是从远程主机拷贝文件至自己的计算机上,"上传"文件就是将文件从自己的计算机中拷贝至远程主机上。用Internet语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件 FTP工作在TCP/IP模型的应用层,基于的传输协议是TCP,FTP客户端和服务器之间的连接是可靠的,面向连接的,为数据的传输提供了可靠的保证。 FTP的主要特征: 1.控制链接是建立在客户协议解释器和服务器协议解释器之间用于交换命令与应答的通信链路。 2.数据连接是传输数据的全双工连接。传输数据可以发生在服务器数据传输过程DTP和客户DTP之间,也可以发生在两个服务器的DTP之间。 文件类型: 1.ASCII码文件类型(默认选择),以NVT ASCII码形式通过数据连接传输 2.EBCDIC文件类型。该文本文件传输方式要求两端都是EBCDIC系统 3.图像文件类型(也成为二进制文件类型),数据发送形式呈现为一个连续的比特流 数据结构: 1.文件结构(File Structure)字节流,无结构; 2.记录结构(Record Structure)文件被划分为记录,用于文本文件; 3.页结构(Page Structure)文件被划分为页,每页有页号和页头。可以进行随机存取或顺序存取 传输方式:FTP的传输模式有流模式、块模式和压缩模式: 1.流模式:数据以字节流的形式传送 -记录结构 -文件结构 2.块模式:文件以块的形式传送,块带有自己的头部分。头字节包括16位计数域和8位描述子代码 3.压缩模式:压缩模式中,因为数据是压缩过的,对于增加带宽有很多好处。
多线程
# 多线程 1.使用线程可以把占据长时间的程序的任务放到后台去处理 2.用户界面可以更加吸引人,这样比如用户点击一个按钮去触发某些事件的处理,可以弹出一个进度条显示处理的进度 3.程序的运行速度可能加快 4.在一些等待的任务实现上入用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占有等等 # 线程与进程的区别 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立运行,必须依存在应用程序中,由应用程序提供多个线程执行控制 每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。 指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存 1.线程可以被抢占(中断) 2.在其他线程正在运行时,线程可以暂时搁置(也成为睡眠) --这就是线程的退让 线程可以分为: 内核线程:由操作系统内核创建和撤销 用户线程:不需要内核支持而在用户程序中实现的线程 常用模块:_thread、 threading **************************************************************************** 1、运行方式不同: 进程不能单独执行,它只是资源的集合。 进程要操作CPU,必须要先创建一个线程。 所有在同一个进程里的线程,是同享同一块进程所占的内存空间。 2,关系 进程中第一个线程是主线程,主线程可以创建其他线程;其他线程也可以创建线程;线程之间是平等的。 进程有父进程和子进程,独立的内存空间,唯一的标识符:pid。 3,速度 启动线程比启动进程快。 运行线程和运行进程速度上是一样的,没有可比性。 线程共享内存空间,进程的内存是独立的。 4,创建 父进程生成子进程,相当于复制一份内存空间,进程之间不能直接访问 创建新线程很简单,创建新进程需要对父进程进行一次复制。 一个线程可以控制和操作同级线程里的其他线程,但是进程只能操作子进程。 5,交互 同一个进程里的线程之间可以直接访问。 两个进程想通信必须通过一个中间代理来实现。 # ############################################################################ 计算机是由硬件和软件组成的。硬件中的CPU是计算机的核心,它承担计算机的所有任务。 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配、任务的调度。 程序时运行在系统上的具有某种功能的软件,比如浏览器,音乐播放器等。 每次执行程序的时候,都会完成一定的功能。 进程就是一个程序在一个数据集上的一次动态执行过程。进程一般由程序、数据集、进程控制块三部分组成。 进程一般由程序、数据集、进程控制块三部分组成。 # 进程与线程之间的关系 线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。 线程可与属于同一进程的其他线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈) # ############################################################################ 构造方法: Thread(group=None, target=None, name=None, args=(), kwargs={}) group: 线程组,目前还没有实现,库引用中提示必须是None; target: 要执行的方法; name: 线程名; args/kwargs: 要传入方法的参数。 ***************************************************************************** isAlive(): 返回线程是否在运行。正在运行指启动后、终止前。 get/setName(name): 获取/设置线程名。 start(): 线程准备就绪,等待CPU调度 is/setDaemon(bool): 获取/设置是后台线程(默认前台线程(False))。(在start之前设置) 如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,主线程和后台线程均停止 如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止 start(): 启动线程。 join([timeout]): 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)。 # ############################################################################ Python中有个全局解释器锁,他就像一把锁所在进程上,保证同一时刻,一个进程中无论有多少线程,只能保证有一个线程出来 随着cpu核数的增加,python中可以创建多个进程,一个进程中有一个全局解释器,这样一个cpu跑一个进程,一个进程同一时刻只能出一个线程,达到并行的目的 线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。 不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。 # 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。 在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。 同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。 # 什么是生产者消费者模式 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。 生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据, 而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。 # 生产者消费者模式的优点 1.解耦 让程序各个模块的关联性降到最低 2.支持并发 3.支持忙闲不均 # Queue队列 , Queue 模块中提供了同步的、线程安全的队列类 先进先出 Queue.qsize() 返回队列的大小 Queue.empty() 如果队列为空,返回True,反之False Queue.full() 如果队列满了,返回True,反之False Queue.full 与 maxsize 大小对应 Queue.get([block[, timeout]])获取队列,timeout等待时间 Queue.get_nowait() 相当Queue.get(False) Queue.put(item) 写入队列,timeout等待时间 Queue.put_nowait(item) 相当Queue.put(item, False) Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号 Queue.join() 实际上意味着等到队列为空,再执行别的操作
from threading import Thread
from queue import Queue
import time
class Pro(Thread):
def __init__(self, n, que):
super(Pro, self).__init__()
self.__Name = n
self.__Queue = que
def run(self):
while True:
if self.__Queue.full():
time.sleep(1)
else:
self.__Queue.put('套套')
time.sleep(1)
# Thread.run(self)
print('%s 生产了一个套套' % self.__Name)
class Con(Thread):
def __init__(self, n, que):
super(Con, self).__init__()
self.__Name = n
self.__Queue = que
def run(self):
while True:
if self.__Queue.empty():
time.sleep(1)
else:
self.__Queue.get('套套')
time.sleep(1)
# Thread.run(self)
print('%s 消费了一个套套' % self.__Name)
if __name__ == '__main__':
q = Queue(maxsize=100)
p = Pro('1', q)
p.start()
p = Pro('2', q)
p.start()
p = Pro('3', q)
p.start()
for item in range(20):
name = 'lj %d' % item
temp = Con(name, q)
temp.start()
# 线程锁:
如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期
(比如我们在每个线程的run方法中加入一个time.sleep(1),并同时输出线程名称,则我们会发现,输出会乱七八糟。
因为可能我们的一个print语句只打印出一半的字符,这个线程就被暂停,执行另一个去了,所以我们看到的结果很乱),这种现象叫做“线程不安全”
于是,Threading模块为我们提供了一个类,Threading.Lock,锁。
我们创建一个该类对象,在线程函数执行前,“抢占”该锁,执行完成后,“释放”该锁,
则我们确保了每次只有一个线程占有该锁。这时候对一个公共的对象进行操作,则不会发生线程不安全的现象了
为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:
threading.RLock。RLock内部维护着一个Lock和一个counter变量,
counter记录了acquire的次数,从而使得资源可以被多次require。
直到一个线程所有的acquire都被release,其他的线程才能获得资源。这里以例1为例,
如果使用RLock代替Lock,则不会发生死锁
threading.BoundedSemaphore(n) 信号量,控制允许n个线程来操作被锁住的操作
# 锁的使用
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([timeout])
#释放
mutex.release()
# ############################################################################
异步
多任务,多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多个运行主线
同步:
多任务,多个任务之间执行的时候要求有先后顺序,必须一个先执行完成之后,另一个才能继续执行,只有一个主线
阻塞:
从调用者的角度出发,如果在调用的时候,被卡主,不能再继续向下运行,需要等待,就说是阻塞
非阻塞:
从调用者的角度出发,如果在调用的时候,没有被卡主,能够继续向下运行,无需等待,就是说非阻塞
threading.Event()机制
类似于一个线程向其他多个线程发号施令的模式,其他线程都会持有一个threading.Event的对象,
这些线程都会等待这个事件的'发生',如果此事件一直不发生,那么这些线程将会阻塞,直至事件的'发生'
例:
有多个线程从Redis队列中读取数据来处理,这些线程都要尝试去连接Redis的服务,一般情况下,如果Redis连接不成功
在各个线程的代码中,都会去尝试重新连接。如果我们想要在启动时确保Redis服务正常,才让那些工作线程去连接Redis服务器
,那么我们就可以采用threading.Event机制来协调各个工作线程的连接操作:主线程中会去尝试连接Redis服务,如果正常的话,
触发事件,各工作线程会尝试连接Redis服务
# 事件触发,异步多线程
import threading
import time
def dev():
print('dev:等人来')
event.wait()
event.clear()
print('dev:pm来了')
print('dev:写一串bug丢给pm')
time.sleep(5)
print('dev:这个需求不可能实现')
event.set()
def pm():
print('pm:去提需求')
event.set() # 标志位 flag变成True
time.sleep(2)
print('pm:waiting for')
while True:
if event.isSet():
print('pm:被开发怼了')
break
else:
print('pm:还没好')
time.sleep(1)
event = threading.Event()
p = threading.Thread(target=dev)
c = threading.Thread(target=pm)
p.start()
c.start()
》》》》》Reslut。。。。。
dev:等人来
pm:去提需求
dev:pm来了
dev:写一串bug丢给pm
pm:waiting for
pm:还没好
pm:还没好
pm:还没好
dev:这个需求不可能实现
pm:被开发怼了
# ############################################################################
# 首先我们需要区分加密和认证这两个基本概念。
加密是将数据资料加密,使得非法用户即使取得加密过的资料,也无法获取正确的资料内容,
所以数据加密可以保护数据,防止监听攻击。其重点在于数据的安全 性。身份认证是用来判断某个身份的真实性,
确认身份后,系统才可以依不同的身份给予不同的权限。其重点在于用户的真实性。两者的侧重点是不同的。
# 公钥私钥的原则:
1.一个公钥对应一个私钥。
2.密钥对中,让大家都知道的是公钥,不告诉大家,只有自己知道的,是私钥。
3.如果用其中一个密钥加密数据,则只有对应的那个密钥才可以解密。
4.如果用其中一个密钥可以进行解密数据,则该数据必然是对应的那个密钥进行的加密。
5.公钥 和 私钥 就是俗称的不对称加密方式,是从以前的对称加密(使用用户名与密码)方式提高
非对称密钥密码的主要应用就是公钥加密和公钥认证,而公钥加密的过程和公钥认证的过程是不一样的,下面我就详细讲解一下两者的区别
公钥
就是给大家用的,可以通过电子邮件发布, 可以通过网站让别人下载,公钥其实是用来加密/验章用的。
私钥
就是自己的,必须非常小心保存,最好加上密码,私钥是用来解密/签章,首先就是key的所有权来说,
私钥是有个人拥有。公钥和私钥的作用是:用公钥加密的内容只能用私钥解密,用私钥加密的内容只能用公钥解密
(1)客户端发起连接请求。
(2)远程主机收到用户的登录请求,把自己的公钥发给客户端。
(3)客户端接收远程主机的公钥,然后使用远程主机的公钥加密登录密码,紧接着将加密后的登录密码连同自己的公钥一并发送给远程主机。
(4)远程主机接收客户端的公钥及加密后的登录密码,用自己的私钥解密收到的登录密码,如果密码正确则允许登录,到此为止双方彼此拥有了对方的公钥,开始双向加密解密。