python-线程\协程\网络编程
1、线程、
线程共享的不安全性
''' 线程共享数据的不安全性 ''' import time from threading import Thread, current_thread, Lock ticket = 10 def sale_ticket(lock): global ticket while True: if ticket > 0: lock.acquire() print('{}正在卖第{}张票'.format(current_thread().name, ticket)) ticket -= 1 lock.release() time.sleep(1) else: print('票卖光啦!') # lock.release() break if __name__ == '__main__': lock = Lock() t1 = Thread(target=sale_ticket, name='1号窗口', args=(lock,)) t2 = Thread(target=sale_ticket, name='2号窗口', args=(lock,)) t3 = Thread(target=sale_ticket, name='3号窗口', args=(lock,)) t4 = Thread(target=sale_ticket, name='4号窗口', args=(lock,)) t4.start() t1.start() t2.start() t3.start()
死锁:两把锁
申请锁的顺序使用不当
开发过程中使用线程,在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,尽管死锁很少发生,但一旦发生就会造成应用的停止响应,程序不做任何事情
避免死锁:
1、重构代码 2、使用timeout参数
import time from threading import Lock, Thread, current_thread def task1(lock1, lock2): if lock1.acquire(): print('{}获取到lock1锁。。。。'.format(current_thread().name)) for i in range(5): print('{}------------------>{}'.format(current_thread().name, i)) time.sleep(0.01) if lock2.acquire(timeout=2): print('{}获取了lock1,lock2'.format(current_thread().name)) lock2.release() lock1.release() def task2(lock1, lock2): if lock2.acquire(): print('{}获取到lock2锁。。。。'.format(current_thread().name)) for i in range(5): print('{}----->{}'.format(current_thread().name, i)) time.sleep(0.01) if lock1.acquire(timeout=2): print('{}获取了lock1,lock2'.format(current_thread().name)) lock1.release() lock2.release() if __name__ == '__main__': lock1 = Lock() lock2 = Lock() t1 = Thread(target=task1, args=(lock1, lock2)) t2 = Thread(target=task2, args=(lock1, lock2)) t1.start() t2.start()
线程同步:
同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。
"同"字从字面上容易理解为一起动作
其实不是,"同"字应是指协同、协助、互相配合。
如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。
线程异步:
在没有共享数据的情况下使用,效率高
进程线程的区别:
定义的不同
进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
区别
一个程序至少有一个进程,一个进程至少有一个线程.
线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
线线程不能够独立执行,必须依存在进程中
优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。
2、线程的信号量
信号量的实现方式:
在内部有一个counter计数器,每当我们 s.acquire()一次,计数器就进行减1处理
每当 s.release()一次,计数器就进行加1处理,当计数器为0的时候其他线程的就处于
等待的状态counter的值就是同一时间可以开启线程的个数
建议使用with
import time from threading import Thread, Semaphore, current_thread # from multiprocessing import Semaphore,Process def task(s): s.acquire() # 减1 with s: for i in range(5): print('{}扫地....{}'.format(current_thread().name, i)) time.sleep(1) s.release() # 加1 if __name__ == '__main__': s = Semaphore(4) for i in range(10): t = Thread(target=task, args=(s,)) t.start()
3、协程:伪线程
实现交替执行
def eat(): for i in range(5): print('gang welcome you') yield def listen(): for i in range(5): print('yinxing de chi bang') yield if __name__ == '__main__': a1 = eat() a2 = listen() while True: try: next(a1) next(a2) except: break
greenlet
''' greenlet gevent ''' from greenlet import greenlet def eat(): for i in range(5): print('坤坤喜欢吃肉饼...') g2.switch() def listen_music(): for i in range(5): print('坤坤喜欢听麻婆豆腐..', i) g1.switch() if __name__ == '__main__': g1 = greenlet(eat) g2 = greenlet(listen_music) g1.switch()
gevent:使用猴子补丁实现交替执行(给耗时的操作贴上标签)
import time import gevent from gevent import monkey monkey.patch_all() def eat(): for i in range(5): print('gang welcome you') time.sleep(0.1) def listen(): for i in range(5): print('yinxing de chi bang') time.sleep(0.1) if __name__ == '__main__': g1 = gevent.spawn(eat) g2 = gevent.spawn(listen) g1.join() g2.join() print('over...')
网络编程
TCP是建立可靠的连接,并且通信双方都可以以流的形式发送数据。
UDP则是面向无连接的协议。使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发送数据包。但是能不能到达就不知道了。
端口号:理解成端口号,识别应用
netstat -an 查看端口的使用
IP地址:电脑在互联网的标识
socket(简称:套接字)
它是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:
它能实现不同带闹闹见的进程间通信,我们网络上各种各种各样的服务大多都是
基于Socket 来完成通信的
参数一:指定协议 AF_INET 或AF_INRT6
参数二:SOCK_STREAM执行使用面向流的TCP协议
from socket import socket,AF_INET,SOCK_STREAM # 创建了一个客户端的socket client = socket(AF_INET,SOCK_STREAM) conn_address = ('10.0.102.132',9001) # 告诉客户端要连接的服务器的地址和端口号 client.connect(conn_address) client.send('gang要来了!!!'.encode('utf-8')) client.close()
''' 创建一个socket服务器 ''' from socket import socket, AF_INET, SOCK_STREAM # 创建socket对象 server = socket(AF_INET, SOCK_STREAM) # 绑定端口号 server.bind(('', 9001)) # 开启监听状态 监听: server.listen(5) while True: socket, addr_info = server.accept() # 阻塞的 # print(socket, addr_info) data = socket.recv(512).decode('utf-8') print('{}发过来的消息式:{}'.format(addr_info[0],data)) socket.close() print(addr_info, '离开了')
另一种方式:
from socket import socket, AF_INET, SOCK_STREAM # 创建了一个客户端的socket client = socket(AF_INET, SOCK_STREAM) conn_address = ('10.0.102.144', 9001) # 告诉客户端要连接的服务器的地址和端口号 client.connect(conn_address) while True: msg = input('客户端输入:') client.send(msg.encode('utf-8')) if msg == '88': break # 接收信息 recv_msg = client.recv(512).decode('utf-8') print('服务器会话:', recv_msg) if recv_msg == '88': break client.close()
from socket import socket, AF_INET, SOCK_STREAM # 创建socket对象 server = socket(AF_INET, SOCK_STREAM) # 绑定端口号 server.bind(('', 9001)) # 开启监听状态 监听: server.listen(5) while True: socket, addr_info = server.accept() # 阻塞的 # print(socket, addr_info) while True: data = socket.recv(512).decode('utf-8') print('客户端说:{}'.format(data)) if data=='88': break msg = input('服务器输入:') socket.send(msg.encode('utf-8')) if msg =='88': break socket.close() print(addr_info, '离开了')
方式三:
from socket import socket, AF_INET, SOCK_STREAM # 创建了一个客户端的socket from threading import Thread client = socket(AF_INET, SOCK_STREAM) conn_address = ('10.0.102.144', 9002) # 告诉客户端要连接的服务器的地址和端口号 client.connect(conn_address) # 任务 def send_msg(sock): while True: msg = input('输入要发送的消息:') sock.send(msg.encode('utf-8')) def recv_msg(sock): while True: data = sock.recv(512).decode('utf-8') if len(data) == 0: break print('\n收到了服务器端的消息:', data) Thread(target=send_msg, args=(client,)).start() Thread(target=recv_msg, args=(client,)).start() # client.close()
from socket import socket, AF_INET, SOCK_STREAM from threading import Thread # 创建socket对象 server = socket(AF_INET, SOCK_STREAM) # 绑定端口号 server.bind(('', 9002)) # 开启监听状态 监听: server.listen(5) # 任务 def send_msg(sock): while True: msg = input('输入要发送的消息:') sock.send(msg.encode('utf-8')) def recv_msg(sock): while True: data = sock.recv(512).decode('utf-8') if len(data) == 0: break print('\n收到了客户端的消息:', data) while True: sock, addr_info = server.accept() # 阻塞的 t1 = Thread(target=send_msg, args=(sock,)) t2 = Thread(target=recv_msg, args=(sock,)) t1.start() t2.start() # sock.close() print(addr_info, '离开了')
浏览器服务端模式:
''' client:浏览器客户端 server端: 浏览器请求: server端响应 ''' from gevent import monkey,socket # 创建socket对象 monkey.patch_all() import gevent server = socket.socket() # 绑定端口号 server.bind(('', 9001)) # 开启监听状态 监听: server.listen(5) def handle_client(sock): print('--------------') recv_data = sock.recv(1024).decode('utf-8') print(recv_data) res_line = 'HTTP/1.1 200 OK\r\n' res_header = 'Content-Type:text/html;charset=utf-8\r\nServer:Apache\r\n' msg = '<h1>你回去吧!!!</h1>' response = res_line+res_header+'\r\n'+msg sock.send(response.encode('utf-8')) sock.close() while True: sock, addr_info = server.accept() # 阻塞的 print(addr_info,'来了') gevent.spawn(handle_client,sock)
# __author:gang # date: 2019/8/15 # coding = utf-8 import socket from multiprocessing import Process import re # 常量名字大写 HTML_ROOT_DIR = './html' class HttpServer(object): def __init__(self): # 创建TCP服务端 self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 服务端口可以重复启动,不会被占用 self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) def bind(self, port1): # 绑定端口 self.server.bind(('', port1)) def start(self): # 设置监听 self.server.listen(128) while True: client_socket, client_address = self.server.accept() client = Process(target=self.handle_client, args=(client_socket,)) client.start() # 在主进程中关闭连接 client_socket.close() def handle_client(self, client_socket): # 获取客户端请求的数据 request_data = client_socket.recv(1024) request_lines = request_data.splitlines() for line in request_lines: print(line) request_start_line = request_lines[0].decode('utf-8') # 使用正则表达式,得到请求的路径或者index.html file_path = re.match(r'\w+ +(/[^ ]*)', request_start_line) file_name = file_path.group(1) print('file_name:', file_name) if '/' == file_name: file_name = '/index.html' try: # 打开文件读取/html/index.html文件内容,以二进制方式 f = open(HTML_ROOT_DIR + file_name, 'rb') except: # 构造相应数据 response_start_line = "HTTP/1.1 404 Not Found File\r\n" response_headers = "Server:My Server\r\n" response_body = "sorry!file not found!" else: # 文件存在,正常处理 file_data = f.read() # 关闭文件 f.close() # 构造相应数据 response_start_line = "HTTP/1.1 200 OK\r\n" response_headers = "Server:My Server\r\n" response_body = file_data.decode("utf-8") # 拼接数据:注意响应头和响应体要加上\r\n response = response_start_line + response_headers + "\r\n" + response_body print("response data:", response) # client_socket.send(bytes(response,"utf-8")) client_socket.send(response.encode("utf-8")) client_socket.close() def main(): http_server = HttpServer() http_server.bind(7788) http_server.start() if __name__ == '__main__': main()
匹配网页的正则表达式
pat = r'(((http|ftp|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?)'