線程使用與網絡編程(筆記)

線程使用與網絡編程

線程簡介

  • 線程可以認爲是輕量級的進程
  • 一個進程中本身就包含一個線程(主線程)
  • 線程是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
    

發佈了18 篇原創文章 · 獲贊 2 · 訪問量 636
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章