8.Python基础学习笔记day8-正则表达式、网络编程、进程与线程

8.Python基础学习笔记day8-正则表达式、网络编程、进程与线程

一、正则表达式
'''
1、
[1,2,3,4]中任意取3个元素排列:
A43 = 4x3x2 = 24

itertools.permutations(list1,r)
参数一:可迭代对象
参数二:取元素的个数
注意:可迭代对象中的元素不能重复[与顺序有关]
功能:返回从可迭代对象中取出r个元素的排列,以迭代器的方式返回。

[1,2,3,4]中任意取3个元素组合:
C43=4x3x2/(3x2x1)= 4

2、itertools.combinations(iter2,r)
 参数一:可迭代对象
 参数二:取元素的个数
 功能:从iter2中取r个的元素的组合
 注意:iter2中不能重复。

3、排列组合
itertools.product(iter2,repeat=n)
 参数一:可迭代对象
 参数二:取元素的位数
 功能:n位中的每一位都可以从可迭代对象取
'''
import itertools

list1 = [1,2,3,4]
res = itertools.permutations(list1,3)
print(len(list(res)))  # 24

res2 = itertools.combinations(list1,3)
print(list(res2))  # [(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]

res3 = itertools.product("1234567890",repeat=6)

_ _ _ _ _ _

print(len(list(res3)))  # 1000000




'''
re.findall(pattren,string)
参数一:正则表达式
参数二:被正则的字符串
功能:对string进行正则匹配,将匹配到的结果作为列表返回
'''
'''
相关正则函数标志位
flags:标志位,用于控制正则表达式的匹配方式,值如下
re.I    忽略大小写     ---常用
re.L    做本地户识别
re.M    多行匹配,影响^和$  ---常用
re.S    是.匹配包括换行符在内的所有字符
re.U    根据Unicode字符集解析字符,影响\w  \W  \b   \B
re.X    使我们以更灵活的格式理解正则表达式
'''

'''
4、元字符:
.                匹配除换行符以外的任意字符
[0123456789]	[]是字符集合,表示匹配方括号中所包含的任意一个字符
[a-z]            匹配任意小写字母
[A-Z]            匹配任意大写字母
[0-9]            匹配任意数字,类似[0123456789]
[0-9a-zA-Z]      匹配任意的数字和字母
[0-9a-zA-Z_]     匹配任意的数字、字母和下划线			
[^0-9]           匹配所有的非数字字符
\d               匹配数字,效果同[0-9]
\D               匹配非数字字符,效果同[^0-9]
\w               匹配数字,字母和下划线,效果同[0-9a-zA-Z_]
\W               匹配非数字,字母和下划线,效果同[^0-9a-zA-Z_]
\s               匹配任意的空白符(空格,换行,回车,换页,制表),效果同[ \f\n\r\t]
\S               匹配任意的非空白符,效果同[^ \f\n\r\t]
'''
string = "you are very good,\r\n12 23 3you are very great!!!"
print(re.findall("you",string))  # ['you', 'you']
print(re.findall(r"[you]",string))  # ['y', 'o', 'u', 'y', 'o', 'o', 'y', 'o', 'u', 'y']
print(re.findall(r".",string))  # 
print(re.findall(r"[0-9]",string))  # ['1', '2', '2', '3', '3']
print(re.findall(r"\d",string))  # ['1', '2', '2', '3', '3']
print(re.findall(r"[^0-9]",string))
print(re.findall(r"\D",string))

print(re.findall(r"[0-9a-zA-Z_]",string))
print(re.findall(r"\w",string))
print(re.findall(r"[^0-9a-zA-Z_]",string))
print(re.findall(r"\W",string))

print(re.findall(r"\s",string))
print(re.findall(r"[ \n\r\t\f]",string))
print(re.findall(r"[^ \n\r\t\f]",string))
print(re.findall(r"\S",string))


'''
5、锚字符(边界字符):
^     行首匹配,和在[]里的^不是一个意思
$     行尾匹配

\A    匹配字符串开始,它和^的区别是,\A只匹配整个字符串的开头,即使在re.M模式下也不会匹配它行的行首
\Z    匹配字符串结束,它和$的区别是,\Z只匹配整个字符串的结束,即使在re.M模式下也不会匹配它行的行尾

\b    匹配一个单词的边界,也就是值单词和空格间的位置
      'er\b'可以匹配never,不能匹配nerve
\B    匹配非单词边界
'''
str1 = "you a very good\nyou a very good"
str2 = " you a very good"
str3 = "good good"
print(re.findall(r"^you",str1,flags=re.M))  # ['you', 'you']
print(re.findall(r"\Ayou",str1,flags=re.M))  # ['you']
print(re.findall(r"good$",str1,flags=re.M))  # ['good', 'good']
print(re.findall(r"good\Z",str1,flags=re.M))  # ['good']
print(re.findall(r"^you",str2))  # []
print(re.findall(r"^good$",str2))  # []
print(re.findall(r"^good$",str3))  # []

print(re.findall(r"er\b","never erver"))  # ['er', 'er']
print(re.findall(r"\ber","never erver"))  # ['er']
print(re.findall(r"\ber\b","never er erver"))  # ['er']
print(re.findall(r"er\B","never er erver"))  # ['er']
print(re.findall(r"\Ber","never er erver"))  # ['er', 'er']


'''
6、匹配多个字符
说明:下方的x、y、z均为假设的普通字符,n、m(非负整数),不是正则表达式的元字符
(xyz)    匹配小括号内的xyz(作为一个整体去匹配)
x?       匹配0个或者1个x
x*       匹配0个或者任意多个x(.* 表示匹配0个或者任意多个字符(换行符除外))
x+       匹配至少一个x
x{n}     匹配确定的n个x(n是一个非负整数)
x{n,}    匹配至少n个x
x{n,m}   匹配至少n个最多m个x。注意:n <= m
x|y      |表示或,匹配的是x或y
'''
str1 = "you are very good you are a great man"
str2 = "youareverygoodyou are a good man"


print(re.findall(r"[a-z]?",str1))
print(re.findall(r"[a-z]*",str2))  # ['youareverygoodyou', '', 'are', '', 'a', '', 'good', '', 'man', '']
print(re.findall(r"[a-z]+",str1))  # ['you', 'are', 'very', 'good', 'you', 'are', 'a', 'great', 'man']
print(re.findall(r"[a-z]{3}",str1))  # ['you', 'are', 'ver', 'goo', 'you', 'are', 'gre', 'man']
print(re.findall(r"[a-z]{3}",str2))  # ['you', 'are', 'ver', 'ygo', 'ody', 'are', 'goo', 'man']
print(re.findall(r"[a-z]{3,}",str2))  # ['youareverygoodyou', 'are', 'good', 'man']
print(re.findall(r"[a-z]{3,4}",str2))  # ['youa', 'reve', 'rygo', 'odyo', 'are', 'good', 'man']
print(re.findall(r"good|great",str1))  # ['good', 'great']


'''
需求写一个正则电话的正则:
电话号码以
1开头
长度11位
全部数字
'''
print("*"*50)
print(re.findall(r"^1\d{10}$","123948293403794"))
print(re.findall(r"^1\d{10}$","12394829340"))
print(re.findall(r"^1\d{10}$","1239482934a"))
print(re.findall(r"^1\d{10}$","1239482934"))

'''
邮箱地址正则:
5-11位数字或者小写字母
@qq.com/@163.com/@sina.cn
'''
print("*"*50)
print(re.match(r"^[0-9a-z]{5,11}(@qq\.com|@163\.com|@sina\.cn)$","11222@qq.cn"))
print(re.match(r"^[0-9a-z]{5,11}(@qq\.com|@163\.com|@sina\.cn)$","11222@qq.com"))
'''
需求:
str1 = "you are a good man,you are a nice man ,you are a great man,you are a..."

提取:you...man
'''
str1 = "you are a good man,you are a nice man ,you are a great man,you are a..."

print(re.findall(r"you.*?man",str1))

'''
.*? 经常一起连用,.代表任意字符 *尽可能多的匹配,?对贪婪匹配的限制
'''
'''
需求:
str2 ="/*  part1  *//*  part2  *//*  part3  */"

提取 /*...*/
'''
str2 ="/*  part1  *//*  part2  *//*  part3  */"
print(re.findall(r"/\*(.*?)\*/",str2))

'''
re.split(pattern,string,maxsplit,flags)
功能:以指定的正则对string进行切片,并且将切片后的结果作为列表返回。
maxsplit:指定最大切片次数,若不指定,全部切片
'''
str1 = "zhangsan    is a good man"
print(str1.split(" "))  # ['zhangsan', '', '', '', 'is', 'a', 'good', 'man']
print(re.split(r" +", str1))  # ['zhangsan', 'is', 'a', 'good', 'man']


'''
re.finditer函数
原型:finditer(pattern, string, flags=0)
参数:
patter: 匹配的正则表达式
string: 要匹配的字符串
flags:标志位,用于控制正则表达式的匹配方式
功能:与findall类似,扫描整个字符串,返回的是一个迭代器
'''
print(list(re.finditer("hello","ohahelloha")))  # [<_sre.SRE_Match object; span=(3, 8), match='hello'>]

'''
re.sub(pattern,repl,string,count)
功能:将匹配到的字符以指定的repl进行替换并且返回替换后的结果
count:指定替换次数,若不指定则默认全部替换
'''
print(re.sub(r"\d+"," ","dd1w3sd2333adfeeft344dfffg3df",count=3))  # ['dd', 'w', 'sd', 'adfeeft', 'dfffg', 'df']

'''
re.match(pattern,string,flags)
参数一:正则表达
参数二:string
参数三:标志位
功能:对string从字符串开始进行匹配,若匹配的上则返回匹配成功的对象,若匹配不上则返回None
注意:这并不是一个完全匹配,若匹配成功之后,string有剩余仍然匹配成功,若要完全匹配,可以在正则
的末尾添加$即可
'''
print(re.match("hello","hellohaha"))

'''
re.search(pattern,string,flags)
参数一:正则表达式
参数二:字符串
参数三:标志位
功能:以指定的正则格式在string进行查找若找到则直接返回第一匹配成功的对象,不再继续匹配。
若找不到返回None
'''
print(re.search("hello","ohahellha"))


'''
 re.compile(pattern,flags)
 参数一:正则表达式
 参数二:标志位
 功能:将正则表达式编译成一个正则对象然后返回。调用的时候调用正则对象即可。
'''
# print(re.match(r"^[0-9a-z]{5,11}(@qq\.com|@163\.com|@sina\.cn)$","11222@qq.cn"))
comobj = re.compile(r"^[0-9a-z]{5,11}(@qq\.com|@163\.com|@sina\.cn)$")
comobj.findall()
二、网络编程
'''
1、TCP网络编程
服务端、客户端、套接字socket、OSI七层模型、三次握手、四次挥手
 
套接字:源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器和服务
 
常用的TCP/IP协议的3种套接字类型如下所示。
流式套接字(SOCK_STREAM):
流式套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送,并按顺序接收。流式套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP(The Transmission Control Protocol)协议。

数据报套接字(SOCK_DGRAM):
数据报套接字提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP(User Datagram Protocol)协议进行数据的传输。由于数据报套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在程序中做相应的处理。

原始套接字(SOCK_RAW):
原始套接字(SOCKET_RAW)允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW

原始套接字与标准套接字(标准套接字指的是前面介绍的流式套接字和数据报套接字)的区别在于:原始套接字可以读写内核没有处理的IP数据包,而流式套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送数据必须使用原始套接字
'''
# 导入socket
import socket
'''
(1)#创建socket对象
参数一:指定的ip协议  ipv4协议
参数二:指定的tcp协议
'''
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

'''
(2)#绑定地址和端口
address地址,以元组的形式传递
域名/ip地址  端口号
网页的端口 80
STMP服务端口  25
'''
sock.bind(("10.36.151.41",9090))

'''
(3)设置最大连接数
'''
sock.listen(6)


'''
(4)接收客户端的链接
accept() -> (socket object, address info)
返回一个元组,含socket对象(套接字),和远程客户端地址address
''' 
clientSocket,clientAddress = sock.accept()

    '''
    clientSocket:
    <socket.socket fd=260, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, 		laddr=('127.0.0.1', 8081), raddr=('127.0.0.1', 61642)>

    clientAddress:
    ('127.0.0.1', 61642)
    '''

'''
(5)获取已连接的客户端发送的数据,返回数据类型是二进制的
'''
clientSocket.recv(1024).en_code("utf-8")


'''
(6)通过套接字向远程客户端发送数据,函数要求所发送数据一定要编码成二进制格式,
返回成功发送数据的字节数(中文占3个字节)
def send(self, data: bytes, flags: int = ...) -> int: ...
'''
sendData = input("输入返回给客户端的数据")
clientSocket.send(sendData.encode("utf-8"))
clientSocket.close()  # 关闭连接
'''
(7)# 客户端使用建立连接
# 参数:是一个元组,第一个元素为要连接的服务器的IP地址,第二个参数为端口
'''
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 获取套接字
client.connect(("127.0.0.1", 8081))  # 连接远程地址,相当于在套接字加入远程地址
print(client)
'''
(8)
<socket.socket fd=276, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 62356), raddr=('127.0.0.1', 8081)>

fd: fd 是(file descriptor),这种一般是BSD Socket的用法,用在Unix/Linux系统上。在Unix/Linux系统下,
    一个socket句柄,可以看做是一个文件,在socket上收发数据,相当于对一个文件进行读写,
    所以一个socket句柄,通常也用表示文件句柄的fd来表示。
    
family:AF_INET(ADDRESS FAMILY Internet 网络地址族[默认ipv4])、AF_INET6(ipv6)
	  (PF_INET,在windows下与AF_INET意义一样)
	  
type:套接字类型
laddr(localaddress):本地地址
raddr(remoteaddress):远程地址
'''  


'''
整体代码
'''
# 服务器端:
import socket
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind(("127.0.0.1",9090))
clientSocket,clientAddress = sock.accept()
while True:
  data = clientSocket.recv(1024)
  print("客户端说:" + data.decode("utf-8"))
  sendData = input("输入返回给客户端的数据")
  print(clientSocket.send(sendData.encode("utf-8")))
  clientSocket.close()  # 关闭连接  
# 客户端
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8081))
while True:
    data = input("请输入给服务器发送的数据")
    client.send(data.encode("utf-8"))

    # 等待接收数据
    info = client.recv(1024)
    print("服务器说:", info.decode("utf-8"))
    
    
'''
2、udp编程
无连接的服务、不可靠传输
直接上代码:
'''    
# 服务端
import socket

udpServer = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 获取udp协议套接字
udpServer.bind(('127.0.0.1', 8900))  # 绑定服务器地址
while True:
    data, addr = udpServer.recvfrom(1024)    # 接受任意客户端发过来的udp请求,返回客户端内容,地址端口(元组)【可以另开线程接受信息】
    print("客户端说:", data.decode("utf-8"))
     udpServer.sendto("你好吗".encode("utf-8"),addr)

# 客户端
import socket

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 获取udp协议套接字
while True:
    data = input("请输入数据")
    client.sendto(data.encode("utf-8"), ('127.0.0.1', 8900))  # 发送内容到指定ip地址端口
    
    fromsdata, address = client.recv(1024)  # 【可以另开线程接受信息】
    print(fromsdata.encode("utf-8"))
三、线程
'''
线程
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

GIL:
GIL是Python解释器(Cpython)引入的概念,在JPython、PyPy中没有GIL。GIL并不是Python的语言缺陷。

GIL定义
GIL,the Global Interpreter Lock,直译为“全局解释锁”

GIL存在原因
CPython在执行多线程的时候并不是线程安全的,为了保证线程间数据的一致性和状态同步的完整性。

GIL的弊端
- GIL对计算密集型的程序会产生影响。因为计算密集型的程序,需要占用系统资源。GIL的存在,相当于始终在进行单线程运算,这样自然就慢了。
- IO密集型影响不大的原因在于,IO,input/output,这两个词就表明程序的瓶颈在于输入所耗费的时间,线程大部分时间在等待,所以它们是多个一起等(多线程)还是单个等(单线程)无所谓的。
  这就好比,你在公交站等公交时,你们竖成一排等公交(单线程)还是沿着马路一字排开等(多线程)是无所谓的。公交车(即input,即输入的资源)没来,哪种方式都是瞎折腾。

如何解决
    1、重写python编译器(官方cpython)如使用:PyPy解释器
    2、调用C语言的链接库

异步:不按照顺序执行,并行执行。ps:可以理解为在不同的线程执行
串行:类似于同步
并行:类似于异步执行【执行任务的数量小于或者等于cpu的数量】
并发:任务的数量大于cpu的数量
同步:按顺序执行代码 ps:可以理解成在同一个线程中执行

'''

'''
1、创建线程的方法1
_thread
'''

    '''
    创建守护线程
    '''
import _thread
import time
import threading

def func(msg):
    print(msg)
    # 获取当前正在执行的线程
    print(threading.current_thread().name)
    
def create_thead():
    '''
    参数一:在子线程中执行的函数
    参数二:子线程执行函数的时候需要的参数,传递参数的时候一定要以元组的方式传参
    守护线程:无论守护是否执行结束,只要主线程结束,守护线程都会自动结束
    '''
    _thread.start_new_thread(func, ("hello world",))

if __name__ == '__main__':
    create_thead()
    # print("main",threading.current_thread())
    time.sleep(1)

    
'''
2、创建线程的方法2
threading
'''    
import threading
import time

def func(msg):
    print(msg)
    print(threading.current_thread().name)


if __name__ == '__main__':
    t = threading.Thread(target=func, args=("hello world",))
    t.start()
    print("main",threading.current_thread().name)

    # time.sleep(5)

'''
3、创建线程的方法3
面向对象
'''     
import threading
class MyThread(threading.Thread):

    def __init__(self,msg):
        super().__init__()
        self.msg = msg

    '''
    若通过继承来创建线程,那么我们必须要重写一个函数,这个函数就是run函数
    在此函数中我们可以写在子线线程中要执行的程序。此函数不需要我们自己手动调用
    当我们启动线程的时候,会自动调用此方法。
    '''
    def run(self):
        print("哈哈")
        print(self.msg)
        print(threading.current_thread().name)


if __name__ == '__main__':
    t = MyThread("hello world")
    t.start()
    
'''
4、线程常用函数
'''    
	'''
    target:线程执行函数
    args:函数需要的参数,必须以元组的方式传参
    name:指定子线程的名字
    daemon:设置是否为守护线程,默认为False,若为守护线程则为True
    '''
	t = threading.Thread(target=func,args=("hello",),name="helloname",daemon=False)

    t.isAlive()  #  功能:判断线程是否还活着
    
    t.getName()  #  获取线程的名字
    t.setName()  #  设置线程的名字
    t.start()  #  开启线程,线程准备就绪
    t.isDaemon()  #  判断某个线程是否为后台线程
    t.join([timeout]): #  阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout
        
'''
5、线程冲突
''' 

import threading
def func():
    global num
    '''
    cpu分配的时间片不足以完成一百万次的加法运算,
    因此,数据还没有被保存,就被其他的线程打断了。
    '''
    for x in range(1000000):
        num += 1

if __name__ == '__main__':
    num = 0
    tlist = []
    for x in range(5):
       t = threading.Thread(target=func)
       t.start()
       tlist.append(t)

    for t in tlist:
        t.join()
    print(num)
    
'''
6、解决线程冲突
线程锁
'''    
import threading

lock = threading.Lock()
'''
# lock.acquire()  #上锁
# lock.release() #释放锁
lock = threading.Lock()
使用 with lock
with内部实现了enter()和exit()执行语句之前调用enter方法,退出的时候调用exit
'''

def func():
    with lock:
        global num
        for x in range(1000000):
            num += 1
    # lock.acquire()  #上锁
    # lock.release() #释放锁

if __name__ == '__main__':
    num = 0
    tlist = []
    for x in range(5):
        t = threading.Thread(target=func)
        t.start()
        tlist.append(t)

    for t in tlist:
        t.join()
    print(num)
    
'''
7、线程锁的问题:
死锁
定义:是指一个资源多次调用,而多次调用方都未能释放该资源就会造成一种互相等待的现象,若无外力作用,它们都将无法推进下去,此时称系统处于死锁状态或者系统产生了死锁。

若存在两个线程:线程A 与线程B
若线程A与线程B都 需要资源1与资源2才能执行
现在线程A拿到了资源1,线程B拿到了资源2,此时就构成了死锁。
若要解决死锁的问题,则此时我们需要使用递归锁。
''' 
'''
使用递归锁来解决死锁的问题。
递归锁的实现原理:
在递归锁中不但存在lock还存在counter计数,每当acquire()一次锁,counter计数就进行
加1处理,每当release() 一次锁,counter计数减1处理,直到counter计数为0的情况下
所有线程重新去抢占资源。
'''

import threading
import time

# A = threading.Lock()
# B = threading.Lock()
# 创建递归锁
rlock = threading.RLock()
class MyThread(threading.Thread):

    def fun1(self):
        rlock.acquire()
        print(self.name,"gotA",time.ctime())
        time.sleep(2)
        rlock.acquire()
        print(self.name,"gotB",time.ctime())
        time.sleep(1)
        rlock.release()
        rlock.release()


    def func2(self):
        rlock.acquire()
        print(self.name,"gotB",time.ctime())
        time.sleep(2)
        rlock.acquire()
        print(self.name,"gotA",time.ctime())
        time.sleep(1)
        rlock.release()
        rlock.release()

    def run(self):
        self.fun1()
        self.func2()


if __name__ == '__main__':
    L = []
    for i in range(5):
        t = MyThread()
        t.start()
        L.append(t)
    for i in L:
        i.join()
    print("over")
    
    
'''
8、信号量的实现方式:
解决,限制同一时间执行线程的个数:
在内部有一个counter计数器,每当我们 s.acquire()一次,计数器就进行减1处理
每当 s.release()一次,计数器就进行加1处理,当计数器为0的时候其他线程的就处于
等待的状态counter的值就是同一时间可以开启线程的个数
建议使用with
'''

s = threading.Semaphore(5)

def func():
    # s.acquire()
    with s:
        time.sleep(1)
        print(threading.current_thread().name)
        print(time.ctime())
    # s.release()


if __name__ == '__main__':
    for x in range(40):
        t = threading.Thread(target=func)
        t.start()    
四、协程及进程
'''
协程:
协程,又称微线程,纤程,协程其实可以认为是比线程更小的执行单元,为啥说他是一个执行单元,因为他自带cpu上下文(地址),这样只要在合适的时机,我们可以把一个协程切换到另一个协程,只要这个过程中保存或者恢复cpu上下文,那么这个程序还是可以运行的。

通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数中都是由开发者自己确定的。

协程与线程的差异:
  协程的特点在一个线程内部执行,与多线程相比,
  协程有什么优点?
  1.最大的优势就是协程有极高的执行效率,因为子程序切换不是线程切换而是程序自身控制,因此,没有线程切换的开销,和多线程比,线程的数量越多,协程的性能就会越明显
  2.第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。因为协程是一个线程执行,那么怎么利用多核cpu呢?
  最简单的方式就是多进程+协程,既充分利用多核,又能充分发挥协程的高效性,可获得极高的性能。
  
  协程的缺点:它不能同时将cpu的多核用上,只能使用一个核;python对协程的支持是通过generator实现的在generator中,我们不但能够通过for循环作用,我们还可以不断调用next()函数,获取由yield语句返回的下一个值
'''


'''
yield关键字的作用:能过多次进入、多次返回,能够暂停函数体中代码的执行

	在python的函数(function)定义中,只要出现了yield表达式(Yield expression),那么事实上定义的是一个generator function, 调用这个generator function返回值是一个generator
	
例如:使用yield生成斐波那契数列
'''
from collections import Iterator

def func(n):
    a = 1
    b = 1
    for x in range(1,n+1):
        if x == 1 or x == 2:
            yield a
        else:
            a,b = a+b,a
            yield a
            
if __name__ == '__main__':            
	 g = func(10)  
     print(isinstance(g, Iterator))  # True  判断是否为迭代器
     for x in g:
        print(x, end=" ")  # 1 1 2 3 5 8 13 21 34 55            
        
        
'''
协程的实现方式1:
yield
'''       
import time

def func():
    while True:
        print("====func===")
        yield
        time.sleep(1)

def func2(func):
    while True:
        print("====func2====")
        next(func)
        time.sleep(1)

if __name__ == '__main__':
    #返回了一个生成器对象
    f = func()   # 有yield,不执行里面的内容,返回生成器对象
    func2(f)
    
'''
===func2===
====func1====
===func2===
====func1====
===func2===
...
'''    


'''
协程的实现方式2:
使用greenlet来实现协程
'''
from greenlet import greenlet
import time


def fun1():
    print("协程1...")
    time.sleep(3)
    g2.switch() #切换到协程g2
    print("节日快乐。。。")


def fun2():
    print("协程2...")
    time.sleep(3)
    g1.switch() #切换到协程g1


if __name__ == '__main__':
    # greenlet(func)  创建协程
    g1 = greenlet(fun1)
    print(g1)
    g2 = greenlet(fun2)

    # 切换并且执行
    g1.switch()
    
'''
协程1...
协程2...
节日快乐。。。
'''    

'''
协程的实现方式3:
使用gevent与monkey实现协程
'''
from gevent import monkey;monkey.patch_all()#导入猴子补丁#可以实现协程的自动切换
import requests
import gevent
import threading
import time

headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'}

def get_data(url):
    response = requests.get(url,headers=headers)
    print(url,"response")
    time.sleep(3)


if __name__ == '__main__':
    #协程爬取数据
    t1 = time.time()
    url_list = [
        'http://www.baidu.com',
        'http://www.qq.com',
        'http://www.ifeng.com',
        'http://www.sina.com.cn',
        'http://www.taobao.com',
    ]
    g_list = []
    for url in url_list:
        g = gevent.spawn(get_data,url)
        g_list.append(g)

    gevent.joinall(g_list)

'''
http://www.ifeng.com response
http://www.qq.com response
http://www.baidu.com response
http://www.sina.com.cn response
http://www.taobao.com response
'''



'''
进程是程序的一次性执行的过程,正在进行的一个过程或者任务,而负责执行任务的是cpu。
进程的生命周期:
当操作系统要完成某个任务的时候它会创建一个进程,当进程完成任务之后,系统就会撤销这个进程,收回它所占用的资源,从创建到撤销的时间段就是进程的生命周期。

进程之间存在并发:
在一个操作系统中,同时会存在多个进程,它们就轮流占用cpu资源。

并行与并发的区别:
无论并行还是并发,在用户看来其实都是同时运行的,不管是进程还是线程,都只是一个任务而已,真正干活的是cpu,而一个cpu(单核)同一时刻只能执行一个任务。

并行:多个任务同时运行,只有具备多个cpu才能真正的实现并行,含有几个cpu,也就意味着同一时间可以执行几个任务。
并发:是伪并行,看起来是同时运行的,实际上是单个cpu在多道程序之间的来回切换。

同步与异步的概念:
同步就是指一个进程在执某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到返回信息才能继续执行下去。

异步:是指进程不需要一直等待下去,而是继续执行下面的操作,不管其他进程的状态,当有消息返回的时候,系统会通知进程来进行处理,这样可以提高执行效率。

比如:打电话的过程就是同步通讯,发短信就是异步通讯
多线程和多进程的关系:
对于计算机密集型应用,应该使用多进程(提高cpu的利用率)
对于IO密集型应用,应该使用多线程,线程的创建比进程的创建开销小的多。
'''
'''
1、进程的创建及与线程的对比
'''
import multiprocessing
import time
import threading


def func(msg):

    # 获取当前正在执行进程名字
    print(multiprocessing.current_process().name)

def func2(msg):

    # 获取当前正在执行进程名字
    print(threading.current_thread().name)


if __name__ == '__main__':
    t1 = time.time()
    plist = []
    for x in range(100):
        p = multiprocessing.Process(target=func,args=("hello",))
        p.start()
        plist.append(p)

    for p in plist:
        p.join()
    t2 = time.time()
    print(t2-t1)  # 7.120407342910767
    
    '''
    分别创建100个进程与100线程,测试他们的耗时时间。
    '''
    tlist = []
    for i in range(100):
        t = threading.Thread(target=func2,args=("nice",))
        t.start()
        tlist.append(t)

    for t in tlist:
        t.join()

    t3 = time.time()
    print(t3-t2)  # 0.027001380920410156

    
'''
2、子进程中要修改全局变量对父进程中的全局变量没有影响
'''    
import multiprocessing
from time import sleep

num = [100]


def run():
    global num  # num = 100
    print("子进程开始")
    num[0] += 1
    print(num)
    print("子进程结束")


if __name__ == "__main__":
    print("父进程开始")

    p = multiprocessing.Process(target=run)
    p.start()
    p.join()

    # 在子进程中要修改全局变量对父进程中的全局变量没有影响
    # 在创建子进程时对全局变量做了一个备份,父进程中的与子进程中的num是完全不同的两个变量
    print("父进程结束--%d"%num[0])
	
    '''
    父进程开始
    子进程开始
    [101]
    子进程结束
    父进程结束--100
    '''
    

'''
3、使用面向对象创建进程
'''    
from multiprocessing import Process
import os, time


class MyProcess(Process):  # 继承Process
    def __init__(self,name):
        Process.__init__(self)
        self.name = name

    def run(self):
        print("子进程(%s-%s)启动" % (self.name, os.getpid()))
        #子进程的功能
        time.sleep(3)
        print("子进程(%s-%s)结束" % (self.name, os.getpid()))


if __name__ == "__main__":
    print("父进程启动")

    #创建子进程
    p = MyProcess("test")
    # 自动调用p进程对象的run方法
    p.start()
    p.join()

    print("父进程结束")
	
    '''
    父进程启动
    子进程(test-18692)启动
    子进程(test-18692)结束
    父进程结束
    '''

    
'''
4-1、进程间的通信1
Queue
'''
from multiprocessing import Process, Queue
import os, time


def write(q):
    print("启动写子进程%s" % (os.getpid()))
    for chr in ["A", "B", "C", "D","\n"]:
        q.put(chr)
        time.sleep(1)
    print("结束写子进程%s" % (os.getpid()))


def read(q):
    print("启动读子进程%s" % (os.getpid()))
    value = None
    while value != "\n":
        value = q.get(True)
        print("value = " + value)
    print("结束读子进程%s" % (os.getpid()))


if __name__ == "__main__":
    #父进程创建队列,并传递给子进程
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))

    pw.start()
    pr.start()


    pw.join()
    pr.join()

    print("父进程结束")

    '''
    启动写子进程36000
    启动读子进程36788
    value = A
    value = B
    value = C
    value = D
    value = 

    结束读子进程36788
    结束写子进程36000
    父进程结束
    '''
'''
4-2:进程间的通信2
Manager: Manager可以创建列表、字典、锁、变量、信号、队列等
manager是自带进程锁,在进行修改的时候不必担心多进程同时进行修改的问题。线程才是真正意义上的共享一个内存区域,但线程不自带锁必须自己加锁。manager比较灵活可以实现不同主机间的进程共享
''' 
import multiprocessing
import os,time
def func1(tlist):
    print("子进程开始:", os.getpid())
    print(len(tlist))
    tlist[-1] = 10000
    time.sleep(1)
    print(tlist)
    print("子进程结束:", os.getpid())


def func2(tlist):
    print("子进程开始:", os.getpid())
    print(len(tlist))
    tlist.reverse()
    time.sleep(1)
    print(tlist)
    print("子进程结束:", os.getpid())


if __name__ == '__main__':
    manager = multiprocessing.Manager()
    list1 = manager.list(range(10))

    p1 = multiprocessing.Process(target=func1, args=(list1,))
    p2 = multiprocessing.Process(target=func2, args=(list1,))
    p1.start()
    p2.start()

    p1.join()
    p2.join()
    
	'''
	子进程开始: 37568
    10
    子进程开始: 37556
    10
    [10000, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    子进程结束: 37568
    [10000, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    子进程结束: 37556
	'''
    
'''
5、进程锁:为了解决多个进程同时访问同一资源(读写文件、数据库)产生的冲突,这时候我们使用进程锁来进行解决,在使用进程锁的时候,需要将进程锁作为参数传递到该进程。
'''
def func(lock):
    with lock:
        print("加锁",multiprocessing.current_process().name)
        time.sleep(2)
        print("释放锁", multiprocessing.current_process().name)


if __name__ == '__main__':

    lock = multiprocessing.Lock()
    plist = []
    for i in range(5):
        p = multiprocessing.Process(target=func,args=(lock,))  # 需要将所传入到子进程中,因为如果父进程结束了,锁就释放了
        p.start()
        plist.append(p)
    # for p in plist:   # 父进程结束不影响子进程运行
    #     p.join()
    print("over")
    
    '''
    over
    加锁 Process-2
    释放锁 Process-2
    加锁 Process-1
    释放锁 Process-1
    加锁 Process-4
    释放锁 Process-4
    加锁 Process-3
    释放锁 Process-3
    加锁 Process-5
    释放锁 Process-5
    '''    

'''
6、信号量:限制同一时间执行的进程的个数。
'''
def func(sem):
    with sem:
        print("开始",multiprocessing.current_process().name)
        time.sleep(1)
        print("结束", multiprocessing.current_process().name)


if __name__ == '__main__':
    sem = multiprocessing.Semaphore(5)
    for x in range(10):
        p = multiprocessing.Process(target=func,args=(sem,))
        p.start()   
        
        '''
        开始 Process-1
        开始 Process-2
        开始 Process-3
        over
        开始 Process-4
        开始 Process-5
        结束 Process-1
        开始 Process-7
        结束 Process-2
        开始 Process-8
        结束 Process-3
        开始 Process-6
        结束 Process-4
        开始 Process-9
        结束 Process-5
        开始 Process-10
        结束 Process-7
        结束 Process-8
        结束 Process-6
        结束 Process-9
        结束 Process-10
        '''
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值