Python3 併發編程2

進程互斥鎖

基本概念

  • 臨界資源: 一次僅容許一個進程使用的資源稱爲臨界資源, 進程間採起互斥的方式, 共享臨界資源
  • 進程互斥: 一個進程正在訪問臨界資源, 另外一個要訪問該資源的進程必須等待
  • 讓併發變成串形, 犧牲了執行效率, 保證了數據的安全
  • 在程序併發執行時, 須要修改時使用

互斥鎖的使用

# base_data--->{"ticket_num": 1}
# 模擬搶票軟件
import json
import time
from multiprocessing import Process


# 查看餘票
def search(user):
    with open('base_data', 'r', encoding='utf-8') as f:
        dic = json.load(f)
        ticket_num = dic.get('ticket_num')
        print(f'用戶{user}正在查看餘票, 當前餘票{ticket_num}張...')


# 購買車票
def buy(user):
    with open('base_data', 'r', encoding='utf-8') as f:
        dic = json.load(f)
        
        # 阻塞
        time.sleep(1)

        if dic.get('ticket_num') > 0:
            dic['ticket_num'] -= 1
            with open('base_data', 'w', encoding='utf-8') as f1:
                json.dump(dic, f1)
            print(f'用戶[{user}]搶票成功!')

        else:
            print(f'用戶[{user}]搶票失敗!')


# 開始搶票
def run(user):
    search(user)
    buy(user)


if __name__ == '__main__':
    for i in range(10):
        # 併發開啓10個子進程
        p = Process(target=run, args=(f'{i}',))
        p.start()
'''
用戶8正在查看餘票, 當前餘票1張...
用戶6正在查看餘票, 當前餘票1張...
用戶3正在查看餘票, 當前餘票1張...
用戶0正在查看餘票, 當前餘票1張...
用戶4正在查看餘票, 當前餘票1張...
用戶2正在查看餘票, 當前餘票1張...
用戶1正在查看餘票, 當前餘票1張...
用戶7正在查看餘票, 當前餘票1張...
用戶5正在查看餘票, 當前餘票1張...
用戶9正在查看餘票, 當前餘票1張...
用戶[8]搶票成功!
用戶[6]搶票成功!
用戶[3]搶票成功!
用戶[4]搶票成功!
用戶[1]搶票成功!
用戶[5]搶票成功!
用戶[2]搶票成功!
用戶[0]搶票成功!
用戶[7]搶票成功!
用戶[9]搶票成功!
'''

使用進程鎖將併發變成串行json

# 模擬搶票軟件
import json
import time
from multiprocessing import Process
from multiprocessing import Lock


# 查看餘票
def search(user):
    with open('base_data', 'r', encoding='utf-8') as f:
        dic = json.load(f)
        ticket_num = dic.get('ticket_num')
        print(f'用戶[{user}]正在查看餘票, 當前餘票{ticket_num}張...')


# 購買車票
def buy(user):
    with open('base_data', 'r', encoding='utf-8') as f:
        dic = json.load(f)

        time.sleep(1)

        if dic.get('ticket_num') > 0:
            dic['ticket_num'] -= 1
            with open('base_data', 'w', encoding='utf-8') as f1:
                json.dump(dic, f1)
            print(f'用戶[{user}]搶票成功!')

        else:
            print(f'用戶[{user}]搶票失敗!')


# 開始搶票
def run(user, mutex):
    # 上鎖
    mutex.acquire()
    search(user)
    buy(user)
    # 解鎖
    mutex.release()


if __name__ == '__main__':
    # 調用Lock()類獲得一個鎖對象
    mutex = Lock()
    for i in range(10):
        # 併發開啓10個子進程
        p = Process(target=run, args=(f'{i}', mutex))
        p.start()
                
'''
用戶[0]正在查看餘票, 當前餘票1張...
用戶[0]搶票成功!
用戶[3]正在查看餘票, 當前餘票0張...
用戶[3]搶票失敗!
用戶[2]正在查看餘票, 當前餘票0張...
用戶[2]搶票失敗!
用戶[4]正在查看餘票, 當前餘票0張...
用戶[4]搶票失敗!
用戶[8]正在查看餘票, 當前餘票0張...
用戶[8]搶票失敗!
用戶[6]正在查看餘票, 當前餘票0張...
用戶[6]搶票失敗!
用戶[7]正在查看餘票, 當前餘票0張...
用戶[7]搶票失敗!
用戶[5]正在查看餘票, 當前餘票0張...
用戶[5]搶票失敗!
用戶[1]正在查看餘票, 當前餘票0張...
用戶[1]搶票失敗!
用戶[9]正在查看餘票, 當前餘票0張...
用戶[9]搶票失敗!

'''

IPC

基本概念

  • inter-process communication 進程間通訊
  • 進程間的數據是相互隔離的, 要想進行進程間的通訊能夠使用隊列

隊列

  • 進程間通訊的一種方式, 支持多進程傳入和取出數據
  • 遵循先進先出的原則
from multiprocessing import Queue

q = Queue(5)  # 隊列中最多存放5個數據

# 填入數據
q.put('數據1')
q.put('數據2')
q.put('數據3')
q.put('數據4')
q.put('數據5')
# q.put('數據6')  # 數據填滿了繼續存放, 程序會被卡住

# 查看隊列是否填滿
print(q.full())

# 隊列滿了, 則會會報錯
# q.put_nowait('數據6')


# 獲取數據, 若隊列中無數據可獲取, 程序會卡住
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())

# 隊列中沒有, 則會報錯
# print(q.get_nowait())

# 判斷隊列是否爲空
print(q.empty())


'''
True
數據1
數據2
數據3
數據4
數據5
True
'''

生產者消費者模型

基本概念

  • 生產者: 生產數據的
  • 消費者: 使用數據
  • 生產者消費者模型: 經過容器來解決生產者和消費者的之間的強耦合問題

代碼實現

from multiprocessing import Process, Queue
import time


# 定義生產者
def producer(q):
    for i in range(5):
        data = f'包子{i}'
        q.put(data)
        print(f'生產了{data}')
        time.sleep(0.1)


# 定義生產者
def consumer(q):
    while True:
        data = q.get()
        print(f'吃了{data}')


if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer, args=(q,))
    p2 = Process(target=consumer, args=(q,))

    p1.start()
    p2.start()
    print('主')

    
'''
主
生產了包子0
吃了包子0
生產了包子1
吃了包子1
生產了包子2
吃了包子2
生產了包子3
吃了包子3
生產了包子4
吃了包子4
'''

線程

基本概念

  • 進程是資源單位, 線程纔是CPU的執行單位, 進行運算調度的最小單位
  • 線程包含在進程之中, 是進程中的實際運做單位
  • 線程開銷要遠小於進程, 能夠節省內存資源
  • 線程之間共享進程中的數據
  • 線程pid爲主進程pid

建立線程

from threading import Thread
import time


# 方式一
def task():
    print('線程開啓')
    time.sleep(1)
    print('線程結束')


if __name__ == '__main__':
    t = Thread(target=task)
    t.start()


# 方式二
class MyThread(Thread):
    def run(self):
        print('線程開啓')
        time.sleep(1)
        print('線程結束')


if __name__ == '__main__':
    t = MyThread()
    t.start()

線程互斥鎖

from threading import Thread, Lock
import time

n = 100


def task(i):
    print(f'線程{i}啓動...')
    global n
    temp = n
    time.sleep(0.1)
    n = temp - 1
    print(n)


if __name__ == '__main__':
    for i in range(10):
        t = Thread(target=task, args=(i + 1,))
        t.start()

'''
線程1啓動...
線程2啓動...
線程3啓動...
線程4啓動...
線程5啓動...
線程6啓動...
線程7啓動...
線程8啓動...
線程9啓動...
線程10啓動...
99
99
99
99
99
99
99
99
99
99
'''
from threading import Thread, Lock
import time

mutex = Lock()

n = 100


def task(i):
    mutex.acquire()
    print(f'線程{i}啓動...')
    global n
    temp = n
    time.sleep(0.1)
    n = temp - 1
    print(n)
    mutex.release()


if __name__ == '__main__':
    for i in range(10):
        t = Thread(target=task, args=(i + 1,))
        t.start()

'''
線程1啓動...
99
線程2啓動...
98
線程3啓動...
97
線程4啓動...
96
線程5啓動...
95
線程6啓動...
94
線程7啓動...
93
線程8啓動...
92
線程9啓動...
91
線程10啓動...
90
'''

相關文章
相關標籤/搜索