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") # 創(chuàng)建三個(gè)線程 threads = [MyThread() for i in range(3)] # 啟動(dòng)三個(gè)線程 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") # 創(chuàng)建三個(gè)線程 threads = [MyThread() for i in range(3)] # 啟動(dòng)三個(gè)線程 for t in threads: t.start() # 一次讓新創(chuàng)建的線程執(zhí)行 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
Jika 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): # 確保先運(yùn)行Seeker中的方法 time.sleep(1) self.cond.acquire() print(self.name + ': 我這兩件商品一起買,可以便宜點(diǎn)嗎') self.cond.notify() self.cond.wait() print(self.name + ': 我已經(jīng)提交訂單了,你修改下價(jià)格') 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() # 釋放對(duì)瑣的占用,同時(shí)線程掛起在這里,直到被 notify 并重新占有瑣。 self.cond.wait() print(self.name + ': 可以的,你提交訂單吧') self.cond.notify() self.cond.wait() print(self.name + ': 好了,已經(jīng)修改了') self.cond.notify() self.cond.wait() print(self.name + ': 嗯,收款成功,馬上給你發(fā)貨') self.cond.release() print(self.name + ': 發(fā)貨商品') cond = threading.Condition() consumer = Consumer(cond, '買家(兩點(diǎn)水)') producer = Producer(cond, '賣家(三點(diǎn)水)') consumer.start() producer.start()
Hasil keluaran adalah seperti berikut:
買家(兩點(diǎn)水): 我這兩件商品一起買,可以便宜點(diǎn)嗎 賣家(三點(diǎn)水): 可以的,你提交訂單吧 買家(兩點(diǎn)水): 我已經(jīng)提交訂單了,你修改下價(jià)格 賣家(三點(diǎn)水): 好了,已經(jīng)修改了 買家(兩點(diǎn)水): 收到,我支付成功了 買家(兩點(diǎn)水): 等待收貨 賣家(三點(diǎn)水): 嗯,收款成功,馬上給你發(fā)貨 賣家(三點(diǎn)水): 發(fā)貨商品
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 isyaratGunakan 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): # 寫數(shù)據(jù)進(jìn)程 for value in ['兩點(diǎn)水', '三點(diǎn)水', '四點(diǎn)水']: print('寫進(jìn) Queue 的值為:{0}'.format(value)) q.put(value) def read(q): # 讀取數(shù)據(jù)進(jìn)程 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:
寫進(jìn) Queue 的值為:兩點(diǎn)水 寫進(jìn) Queue 的值為:三點(diǎn)水 從 Queue 讀取的值為:兩點(diǎn)水 寫進(jìn) Queue 的值為:四點(diǎn)水 從 Queue 讀取的值為:三點(diǎn)水 從 Queue 讀取的值為:四點(diǎn)水
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.