線程使用與網絡編程
線程簡介
- 線程可以認爲是輕量級的進程
- 一個進程中本身就包含一個線程(主線程)
- 線程是CPU分配時間(任務調度)的基本單位,調度是操作系統的事。
- 進程之間數據是獨立的,線程之間數據是共享的
- 一個進程實現多任務,只要創建多個線程就可以了
- 進程的開銷較大,而線程的開銷較小
- GIL:Global Interpreter Lock的簡寫
- 他本身是CPython解釋器的特性,只是因爲大多數情況下CPython都是默認的解釋器
- 因此很多人誤認爲GIL是python本身的特性
- python的多線程效率受到很大影響,幾乎等於單線程的效率
- 線程模塊
_thread
:低級模塊,使用不夠方便和靈活,極少用到threading
:高級模塊,是對_thread
模塊的封裝,推薦使用
_thread
-
示例:
import _thread import time def loop(num): print('子線程開始') print('參數:', num) print('子線程結束') if __name__ == '__main__': print('主線程開始') # 創建並啓動一個子線程 _thread.start_new_thread(loop, (100,)) time.sleep(2) print('主線程結束')
threading
-
基本使用
import threading import time def run(file): print(file, '正在下載') ct = threading.current_thread() print('子線程中當前線程:', ct.name) for i in range(1, 6): time.sleep(1) print('已下載{}%...'.format(i*20)) print(file, '下載完成') if __name__ == '__main__': print('主線程開始') # 獲取主線程 mt = threading.main_thread() print('主線程:', mt.name) # 獲取當前線程 ct = threading.current_thread() print('當前線程:', ct.name) # 創建子線程 thr = threading.Thread(target=run, args=('美女.png',), name='圖片下載') # 查看當前進程中的活躍線程數量 print('活躍線程數量:', threading.active_count()) print('子進程激活狀態:', thr.is_alive()) # 啓動線程 thr.start() print('活躍線程數量:', threading.active_count()) print('子進程激活狀態:', thr.is_alive()) # 查看線程信息 print('線程信息:', threading.enumerate()) # 等待子線程結束 thr.join() print('主線程結束')
-
數據共享:全局變量可以共享
import threading # 全局變量可以在線程之間共享 num = 100 def run(): global num num += 10 print('子線程中修改全局變量num') if __name__ == '__main__': thr = threading.Thread(target=run) thr.start() thr.join() print('主線程結束', num)
- 線程鎖
import threading num = 100 def run(n): global num for i in range(100000): ''' # 獲取鎖 lock.acquire() try: num += n num -= n except Exception as e: print('出現異常', e) finally: # 釋放鎖 lock.release() ''' # 簡化方案 with lock: num += n num -= n if __name__ == '__main__': # 線程鎖 lock = threading.Lock() thr1 = threading.Thread(target=run, args=(5,)) thr2 = threading.Thread(target=run, args=(10,)) thr1.start() thr2.start() thr1.join() thr2.join() print(num)
-
線程類
from threading import Thread from time import sleep class EmailThread(Thread): def __init__(self, email): super().__init__() self.email = email def run(self): print('發送郵件開始') print('發送郵件:', self.email) for i in range(1, 6): sleep(1) print('發送進度:{}%'.format(i*20)) print('發送郵件完成') if __name__ == '__main__': print('主線程開始') et = EmailThread('1024,節日快樂!') et.start() et.join() print('主線程結束')
-
定時線程:延時線程
import threading import os def run(): os.system('calc') if __name__ == '__main__': # 創建定時任務 t = threading.Timer(3, run) t.start() t.join()
-
信號傳遞:可以理解爲一個線程控制另一線程的執行
import threading from time import sleep def run(num): for i in range(num): # 等待條件成立,條件不成立會阻塞 e.wait() print('子線程執行:', i) # 清除條件,爲下次做準備 e.clear() if __name__ == '__main__': # 創建對象 e = threading.Event() n = 3 thr = threading.Thread(target=run, args=(n,)) thr.start() for i in range(n): sleep(1) print('主線程執行:', i) # 設置條件,wait處將不再阻塞 e.set()
網絡相關概念
-
OSI七層模型:Open System Interconnection,參考模型是國際標準化組織(ISO)制定的一個用於計算機或通信系統間互聯的標準體系,一般稱爲OSI參考模型或七層模型。
-
TCP/IP:在OSI七層模型基礎上簡化出來的一套網絡協議簇(四層模型),得到了廣泛使用
-
TCP協議:傳輸控制協議
- 是有連接的,數據傳輸安全可靠
- 三次握手、四次揮手,數據檢查,傳輸速度稍慢
- 窗口不再每次都檢查,隔幾次檢查一次,源於現在準確度大大提高
-
UDP協議:用戶數據報(User datagram)協議
- 無連接的,數據不可靠
- 傳輸速度稍快
-
IP地址:計算機的唯一標識
windows
系統查看:ipconfig
-
ping
:檢查網絡連通性- 示例:
ping 域名/IP
- 示例:
-
端口號:每個應用對應一個端口號
- 範圍:0~65535
- 公認端口:0~1023
協議 端口 http 80 https 443 smtp 25 ftp 21 ssh 22 mysql 3306 redis 6379 - 其他端口:1024~65535
-
網絡編程核心:
- 身份:
IP+PORT
- 類庫:
socket
- 身份:
TCP協議
-
說明:面向連接的、數據可靠、三次握手、四次揮手、數據校驗、傳輸稍慢
-
原理:
見圖片tcp.png
- 示例1:模擬http協議,向百度服務器發送請求
import socket
# family:選擇協議族,socket.AF_INET表示IPv4
# type:傳輸層協議,socket.SOCK_STREAM表示TCP
skt = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 與服務器建立連接
skt.connect(('www.baidu.com', 80))
# 向服務器發送數據
skt.send(b'GET / HTTP/1.1\r\nHost:www.baidu.com\r\nConnection:close\r\n\r\n')
# 接收數據
data = []
while True:
temp = skt.recv(1024)
if temp:
data.append(temp)
else:
break
# 關閉套接字
skt.close()
# 拼接接收的內容
content = b''.join(data).decode('utf-8')
# print(content)
header, body = content.split('\r\n\r\n', 1)
# print(header)
print(body)
- 示例2:
echo
服務器,接收到什麼就返回什麼
# 服務器端代碼
import socket
import multiprocessing
def echo(skt, addr):
while True:
recv_data = skt.recv(1024)
print(f"{addr},{recv_data.decode('utf-8')}")
skt.send(recv_data)
if recv_data == b'byebye':
break
skt.close()
if __name__ == '__main__':
skt = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
skt.bind(('10.8.158.17', 8888))
skt.listen(100)
while True:
client_skt, client_addr = skt.accept()
p = multiprocessing.Process(target=echo, args=(client_skt, client_addr))
p.start()
client_skt.close()
# 客戶端代碼
import socket
skt = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
skt.connect(('10.8.158.17', 8888))
while True:
data = input('>>>: ')
skt.send(data.encode('utf-8'))
recv_data = skt.recv(1024)
print(f"服務器:{recv_data.decode('utf-8')}")
if recv_data == b'byebye':
break
skt.close()
###UDP協議
-
說明:面向無連接,數據不可靠,傳輸速度稍快,適合於對數據要求不太嚴格的情況
-
原理:見圖片
udp.png
- 示例1:udp協議模擬發送飛秋數據
import socket
# 創建套接字
skt = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
# 準備數據 '版本號:數據報序號:用戶名:主機名:命令:消息內容[:附加數據]'
data = '1:12345:Jerry:windows:32:大家好!'
# 發送數據
skt.sendto(data.encode('utf-8'), ('10.8.158.255', 2425))
練習
-
實現udp協議的echo服務器
# 服務器代碼 import socket skt = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) skt.bind(('10.8.158.17', 8888)) while True: data, addr = skt.recvfrom(1024) print(f"客戶端{addr}:{data.decode('utf-8')}") skt.sendto(data, addr)
# 客戶端代碼 import socket skt = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) while True: data = input('>>>: ') skt.sendto(data.encode('utf-8'), ('10.8.158.17', 8888)) data, addr = skt.recvfrom(1024) print(f"服務器:{data.decode('utf-8')}") if data == b'byebye': break