1. 程式人生 > 其它 >訊息佇列和執行緒

訊息佇列和執行緒

訊息佇列

佇列:先進先出(使用平率很高)
堆疊:先進後出(特定常見下用)
from multiprocessing import Queue

q = Queue(5)	# 自定義佇列的長度
q.put(111)
q.put(222)
# 放入資料
print(q.full())  # False 判斷佇列是否滿了
print(q.get())  # 拿資料
print(q.empty())  # False 判斷是否空了


IPC機制(程序間通訊)

"""
1.主程序與子程序資料互動
2.兩個子程序資料互動
本質:不同記憶體空間中的程序資料互動
"""
from multiprocessing import Process, Queue


def producer(q):
    # print('子程序producer從佇列中取值>>>:', q.get())
    q.put('子程序producer往佇列中新增值')

def consumer(q):
    print('子程序consumer從佇列中取值>>>:', q.get())


if __name__ == '__main__':
    q = Queue()
    p = Process(target=producer, args=(q, ))
    p1 = Process(target=consumer, args=(q,))
    p.start()
    p1.start()
    # q.put(123)  # 主程序往佇列中存放資料123
    print('主程序')

生產者消費者模型

# 生產者
	負責生產/製作資料
# 消費者
	負責消費/處理資料
from multiprocessing import Process, Queue, JoinableQueue
import time
import random

def producer(name, food, q):
    for i in range(5):
        data = f'{name}做出了{food}{i}'
        print(data)
        time.sleep(random.randint(1, 3))
        q.put(data)

def consumer(name, q):
    while True:
        food = q.get()
        time.sleep(random.random())
        print(f'{name}吃了{food}')
        q.task_done()

if __name__ == '__main__':
    q = JoinableQueue()
    p1 = Process(target=producer, args=('大廚李鬆穩', '紅燒排骨', q))
    p2 = Process(target=producer, args=('大廚尤濤', '水煮白菜', q))
    c1 = Process(target=consumer, args=('穩穩', q))
    c2 = Process(target=consumer, args=('龍龍', q))
    c1.daemon = True
    c2.daemon = True
    p1.start()
    p2.start()
    c1.start()
    c2.start()
    p1.join()
    p2.join()
    q.join()

執行緒理論

# 什麼是執行緒
程序:資源單位
執行緒:執行單位
# 為什麼要有執行緒
	開設執行緒的消耗遠遠小於程序
  	開程序
      1.申請記憶體空間
      2.拷貝程式碼
   	開執行緒
    	一個程序內可以開設多個執行緒 無需申請記憶體空間、拷貝程式碼
      一個程序內的多個執行緒資料是共享的

開設執行緒的兩種方式

"""程序與執行緒的程式碼實操幾乎是一樣的"""
from threading import Thread
import time


def task(name):
    print(f'{name} is running')
    time.sleep(3)
    print(f'{name} is over')

# 建立執行緒無需在__main__下面編寫 但是為了統一 還是習慣在子程式碼中寫
t = Thread(target=task, args=('jason', ))
t.start()  # 建立執行緒的開銷極小 幾乎是一瞬間就可以建立
print('主執行緒')


class MyThread(Thread):
    def __init__(self, username):
        super().__init__()
        self.username = username
    def run(self):
        print(f'{self.username} jason is running')
        time.sleep(3)
        print(f'{self.username} is over')

t = MyThread('jasonNB')
t.start()
print('主執行緒')

執行緒實現TCP服務端的併發

import socket
from threading import Thread

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen()


def talk(sock):
    while True:
        data = sock.recv(1024)
        print(data.decode('utf8'))
        sock.send(data.upper())


while True:
    sock, addr = server.accept()
    # 每類一個客戶端就建立一個執行緒做資料互動
    t = Thread(target=talk, args=(sock,))
    t.start()

執行緒join方法

from threading import Thread
import time


def task(name):
    print(f'{name} is running')
    time.sleep(3)
    print(f'{name} is over')


t = Thread(target=task, args=('jason', ))
t.start()
t.join()  # 主執行緒程式碼等待子執行緒程式碼執行完畢之後再往下執行
print('主執行緒')

同一個程序內的多個執行緒資料共享

from threading import Thread
money = 1000
def tack():
    global money
    money = 10

t = Thread(target=tack)
t.start()
t.join()
print(money)

執行緒物件屬性和方法

1.驗證一個程序下的多個執行緒是否真的處於一個程序
	驗證確實如此
2.統計程序下活躍的執行緒數
	active_count()  # 注意主執行緒也算!!!
3.獲取執行緒的名字
	1.current_thread().name
  	MainThread  				 主執行緒
    Thread-1、Thread-2		子執行緒
    2.self.name

守護執行緒

from threading import Thread
import time


def task(name):
    print(f'{name} is running')
    time.sleep(3)
    print(f'{name} is over')

t1 = Thread(target=task, args=('jason',))
t2 = Thread(target=task, args=('kevin',))
t1.daemon = True
t1.start()
t2.start()
print('主執行緒')

GIL全域性直譯器鎖

# 官方文件
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly 
because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
"""
1.回顧
	python直譯器的類別有很多
		Cpython Jpython Ppython
	垃圾回收機制
		應用計數、標記清除、分代回收
    
GIL只存在於CPython直譯器中,不是python的特徵
GIL是一把互斥鎖用於阻止同一個程序下的多個執行緒同時執行
原因是因為CPython直譯器中的垃圾回收機制不是執行緒安全的

反向驗證GIL的存在 如果不存在會產生垃圾回收機制與正常執行緒之間資料錯亂
GIL是加在CPython直譯器上面的互斥鎖
同一個程序下的多個執行緒要想執行必須先搶GIL鎖 所以同一個程序下多個執行緒肯定不能同時執行 即無法利用多核優勢


強調:同一個程序下的多個執行緒不能同時執行即不能利用多核優勢
	很多不懂python的程式設計師會噴python是垃圾 速度太慢 有多核都不能用

反懟:雖然用一個程序下的多個執行緒不能利用多核優勢 但是還可以開設多程序!!!

再次強調:python的多執行緒就是垃圾!!!

反懟:要結合實際情況 
	如果多個任務都是IO密集型的 那麼多執行緒更有優勢(消耗的資源更少)
		多道技術:切換+儲存狀態
	如果多個任務都是計算密集型 那麼多執行緒確實沒有優勢 但是可以用多程序
		CPU越多越好
	
以後用python就可以多程序下面開設多執行緒從而達到效率最大化
"""
1.所有的解釋型語言都無法做到同一個程序下多個執行緒利用多核優勢
2.GIL在實際程式設計中其實不用考慮