pengaturcaraan berbilang benang

Sebenarnya, selepas benang dibuat, benang tidak sentiasa mengekalkan keadaan keadaannya secara kasar seperti berikut:

Baharu dicipta

Sedia boleh dijalankan. Menunggu penjadualan

Berlari untuk berlari

Tersekat. Penyekatan mungkin berlaku dalam Wait Locked Sleeping

Dead

Threads mempunyai keadaan dan jenis yang berbeza. Ia boleh dibahagikan secara kasar kepada:

Benang utama

Sub-benang

Benang Daemon (benang latar belakang)

Benang latar depan

Setelah memahami perkara ini secara ringkas, kita mula melihat penggunaan kod khusus.

1. Penciptaan benang

Python menyediakan dua modul untuk operasi berbilang benang, iaitu benang dan benang

Yang pertama ialah modul peringkat rendah, digunakan untuk operasi peringkat rendah, dan secara amnya tidak sesuai untuk pembangunan peringkat aplikasi yang biasa digunakan.

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import time
import threading
class MyThread(threading.Thread):
    def run(self):
        for i in range(5):
            print('thread {}, @number: {}'.format(self.name, i))
            time.sleep(1)
def main():
    print("Start main threading")
    # 创建三个线程
    threads = [MyThread() for i in range(3)]
    # 启动三个线程
    for t in threads:
        t.start()
    print("End Main threading")
if __name__ == '__main__':
    main()

Hasil berjalan:

Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
End Main threading
thread Thread-2, @number: 1
thread Thread-1, @number: 1
thread Thread-3, @number: 1
thread Thread-1, @number: 2
thread Thread-3, @number: 2
thread Thread-2, @number: 2
thread Thread-2, @number: 3
thread Thread-3, @number: 3
thread Thread-1, @number: 3
thread Thread-3, @number: 4
thread Thread-2, @number: 4
thread Thread-1, @number: 4

Perhatikan bahawa hasil keluaran persekitaran berbeza di sini pasti berbeza.

2. Penggabungan benang (kaedah bergabung)

Daripada hasil yang dicetak dalam contoh di atas, selepas utas utama tamat, utas anak masih berjalan. Oleh itu, kita memerlukan utas utama untuk menunggu benang kanak-kanak selesai dijalankan sebelum keluar.

Pada masa ini, anda perlu menggunakan kaedah join.

Dalam contoh di atas, tambahkan sekeping kod seperti berikut:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import time
import threading
class MyThread(threading.Thread):
    def run(self):
        for i in range(5):
            print('thread {}, @number: {}'.format(self.name, i))
            time.sleep(1)
def main():
    print("Start main threading")
    # 创建三个线程
    threads = [MyThread() for i in range(3)]
    # 启动三个线程
    for t in threads:
        t.start()
    # 一次让新创建的线程执行 join
    for t in threads:
        t.join()
    print("End Main threading")
if __name__ == '__main__':
    main()

Daripada hasil yang dicetak, dapat dilihat dengan jelas bahawa berbanding dengan hasil cetakan dalam contoh di atas, utas utama tamat selepas menunggu benang anak untuk habis berlari.

Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
thread Thread-1, @number: 1
thread Thread-3, @number: 1
thread Thread-2, @number: 1
thread Thread-2, @number: 2
thread Thread-1, @number: 2
thread Thread-3, @number: 2
thread Thread-2, @number: 3
thread Thread-1, @number: 3
thread Thread-3, @number: 3
thread Thread-3, @number: 4
thread Thread-2, @number: 4
thread Thread-1, @number: 4
End Main threading

3. Penyegerakan benang dan kunci mutex

Menggunakan pemuatan benang untuk mendapatkan data biasanya mengakibatkan data tidak segerak. Sudah tentu, kita boleh mengunci sumber pada masa ini, iaitu, utas yang mengakses sumber perlu mendapatkan kunci sebelum mengaksesnya.

Modul benang memberikan kita fungsi Kunci.

lock = threading.Lock()

Dapatkan kunci dalam benang

lock.acquire()

Selepas digunakan, kita pasti perlu melepaskan kunci

lock.release()

Sudah tentu, untuk menyokong berbilang permintaan untuk sumber yang sama dalam benang yang sama, Python menyediakan kunci masuk semula (RLock ). RLock secara dalaman mengekalkan Lock dan pemboleh ubah pembilang Pembilang merekodkan bilangan perolehan, supaya sumber boleh diperlukan beberapa kali. Sehingga semua perolehan benang dikeluarkan, utas lain boleh mendapatkan sumber tersebut.

Jadi bagaimana untuk mencipta kunci masuk semula? Ini juga soal kod:

r_lock = threading.RLock()

4. Pembolehubah keadaan

Kunci praktikal boleh mencapai penyegerakan benang, tetapi dalam persekitaran yang lebih kompleks, beberapa pertimbangan bersyarat perlu dibuat pada kunci. Python menyediakan objek Keadaan. Objek Condition boleh digunakan untuk memproses data selepas peristiwa tertentu dicetuskan atau syarat tertentu dipenuhi Selain kaedah perolehan dan kaedah pelepasan objek Lock, Condition juga menyediakan kaedah tunggu dan maklumkan. Benang pertama memperoleh kunci pembolehubah keadaan. Jika syarat tidak mencukupi, utas menunggu Jika syarat dipenuhi, utas akan dilaksanakan, malah ia boleh memberitahu utas lain. Urutan lain dalam keadaan menunggu akan menilai semula syarat selepas menerima pemberitahuan.

Pembolehubah keadaan boleh dilihat sebagai benang yang berbeza memperoleh kunci satu demi satu Jika syarat tidak dipenuhi, ia boleh difahami sebagai dibuang ke dalam kolam menunggu (Kunci atau RLock). Maklumkan secara langsung rangkaian lain dan kemudian nilai semula keadaan. Proses ini diulang secara berterusan untuk menyelesaikan masalah penyegerakan yang kompleks. Model ini sering digunakan dalam model pengeluar komunikasi

35192f0e58595b25a0c422efd13ef05.pngJika program Terdapat berbilang utas di dalamnya, dan utas ini sudah semestinya perlu berkomunikasi antara satu sama lain. Jadi bagaimana kita bertukar maklumat atau data dengan selamat antara rangkaian ini?

Mungkin cara paling selamat untuk menghantar data dari satu thread ke thread lain ialah menggunakan baris gilir dari pustaka baris gilir. Mencipta objek Baris yang dikongsi oleh berbilang benang, yang menambah atau mengalih keluar elemen daripada baris gilir menggunakan operasi put() dan get().

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import threading, time
class Consumer(threading.Thread):
    def __init__(self, cond, name):
        # 初始化
        super(Consumer, self).__init__()
        self.cond = cond
        self.name = name
    def run(self):
        # 确保先运行Seeker中的方法
        time.sleep(1)
        self.cond.acquire()
        print(self.name + ': 我这两件商品一起买,可以便宜点吗')
        self.cond.notify()
        self.cond.wait()
        print(self.name + ': 我已经提交订单了,你修改下价格')
        self.cond.notify()
        self.cond.wait()
        print(self.name + ': 收到,我支付成功了')
        self.cond.notify()
        self.cond.release()
        print(self.name + ': 等待收货')
class Producer(threading.Thread):
    def __init__(self, cond, name):
        super(Producer, self).__init__()
        self.cond = cond
        self.name = name
    def run(self):
        self.cond.acquire()
        # 释放对琐的占用,同时线程挂起在这里,直到被 notify 并重新占有琐。
        self.cond.wait()
        print(self.name + ': 可以的,你提交订单吧')
        self.cond.notify()
        self.cond.wait()
        print(self.name + ': 好了,已经修改了')
        self.cond.notify()
        self.cond.wait()
        print(self.name + ': 嗯,收款成功,马上给你发货')
        self.cond.release()
        print(self.name + ': 发货商品')
cond = threading.Condition()
consumer = Consumer(cond, '买家(两点水)')
producer = Producer(cond, '卖家(三点水)')
consumer.start()
producer.start()

Hasil keluaran adalah seperti berikut:

买家(两点水): 我这两件商品一起买,可以便宜点吗
卖家(三点水): 可以的,你提交订单吧
买家(两点水): 我已经提交订单了,你修改下价格
卖家(三点水): 好了,已经修改了
买家(两点水): 收到,我支付成功了
买家(两点水): 等待收货
卖家(三点水): 嗯,收款成功,马上给你发货
卖家(三点水): 发货商品

Python juga menyediakan objek Acara untuk komunikasi antara benang Ia adalah bendera isyarat yang ditetapkan oleh benang Jika bendera isyarat adalah benar, benang lain menunggu sehingga isyarat dihubungi . Objek Acara melaksanakan mekanisme komunikasi benang yang mudah Ia menyediakan isyarat tetapan, isyarat kosong, menunggu, dll. untuk merealisasikan komunikasi antara benang.

Tetapkan isyarat

Gunakan kaedah set() Acara untuk menetapkan bendera isyarat di dalam objek Acara kepada benar. Objek Acara menyediakan kaedah isSe() untuk menentukan status bendera isyarat dalamannya. Apabila menggunakan kaedah set() objek acara, kaedah isSet() mengembalikan true

Kosongkan isyarat


Gunakan kaedah clear() objek Acara untuk mengosongkan bendera isyarat di dalam objek Acara, iaitu, tetapkannya kepada false. Apabila menggunakan Peristiwa Selepas kaedah yang jelas, kaedah isSet() mengembalikan palsu

Menunggu

Kaedah menunggu objek peristiwa hanya akan dilaksanakan dengan cepat dan melengkapkan pulangan apabila isyarat dalaman adalah benar. Apabila bendera isyarat dalaman objek Acara adalah palsu, kaedah tunggu menunggu sehingga ia benar sebelum kembali.

Contoh:

# -*- coding: UTF-8 -*-
from queue import Queue
from threading import Thread
isRead = True
def write(q):
    # 写数据进程
    for value in ['两点水', '三点水', '四点水']:
        print('写进 Queue 的值为:{0}'.format(value))
        q.put(value)
def read(q):
    # 读取数据进程
    while isRead:
        value = q.get(True)
        print('从 Queue 读取的值为:{0}'.format(value))
if __name__ == '__main__':
    q = Queue()
    t1 = Thread(target=write, args=(q,))
    t2 = Thread(target=read, args=(q,))
    t1.start()
    t2.start()

Hasil keluaran adalah seperti berikut:

写进 Queue 的值为:两点水
写进 Queue 的值为:三点水
从 Queue 读取的值为:两点水
写进 Queue 的值为:四点水
从 Queue 读取的值为:三点水
从 Queue 读取的值为:四点水

6 Benang latar belakang

Secara lalai, selepas utas utama keluar, walaupun utas anak tidak bercantum. Kemudian selepas utas utama tamat, utas anak masih akan terus dilaksanakan. Jika anda mahu utas utama keluar, sub-benangnya juga akan keluar dan tidak lagi dilaksanakan, anda perlu menetapkan sub-benang sebagai utas latar belakang. Python menyediakan kaedah setDeamon.

Meneruskan pembelajaran
  • Cadangan kursus
  • Muat turun perisian kursus