Bagaimana cara berbagi variabel global dengan benang?
Saya Python contoh kode ini:
from threading import Thread
import time
a = 0 #global variable
def thread1(threadname):
#read variable "a" modify by thread 2
def thread2(threadname):
while 1:
a += 1
time.sleep(1)
thread1 = Thread( target=thread1, args=("Thread-1", ) )
thread2 = Thread( target=thread2, args=("Thread-2", ) )
thread1.join()
thread2.join()
Saya don't tahu bagaimana untuk mendapatkan dua benang untuk berbagi satu variabel.
Anda hanya perlu untuk menyatakan a
sebagai global di thread2
, sehingga anda tidak't memodifikasi sebuah a
itu adalah lokal untuk fungsi itu.
def thread2(threadname):
global a
while True:
a += 1
time.sleep(1)
Di thread1
, anda don't perlu melakukan sesuatu yang khusus, asalkan anda don't mencoba untuk mengubah nilai dari a
(yang akan membuat variabel lokal bahwa bayangan yang global; menggunakan global
jika anda perlu)>
def thread1(threadname):
#global a # Optional if you treat a as read-only
while a < 10:
print a
Dalam fungsi:
a += 1
akan diterjemahkan oleh compiler sebagai menetapkan ke => Membuat variabel lokal a
, yang adalah bukan apa yang anda inginkan. Mungkin akan gagal dengan yang tidak diinisialisasi
kesalahan sejak (lokal) yang memang belum diinisialisasi:
>>> a = 1
>>> def f():
... a += 1
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment
Anda mungkin mendapatkan apa yang anda inginkan dengan (sangat disukai, dan untuk alasan yang baik) global
kata kunci, seperti:
>>> def f():
... global a
... a += 1
...
>>> a
1
>>> f()
>>> a
2
Pada umumnya namun, anda harus avoid menggunakan variabel global yang menjadi sangat cepat keluar dari tangan. Dan hal ini terutama berlaku untuk program multithreaded, di mana anda don't memiliki mekanisme sinkronisasi untuk anda thread1
untuk tahu kapan a
telah dimodifikasi. Singkatnya: benang ribet, dan anda tidak bisa berharap untuk memiliki pemahaman intuitif dari urutan di mana peristiwa terjadi ketika dua (atau lebih) benang bekerja pada nilai yang sama. Bahasa, compiler, OS, prosesor... SEMUA bisa memainkan peran, dan memutuskan untuk mengubah urutan operasi untuk kecepatan, kepraktisan, atau alasan lain.
Cara yang tepat untuk hal semacam ini adalah dengan menggunakan Python alat berbagi (kunci dan teman-teman), atau lebih baik, berkomunikasi data melalui Antri bukannya berbagi itu, misalnya seperti ini:
from threading import Thread
from queue import Queue
import time
def thread1(threadname, q):
#read variable "a" modify by thread 2
while True:
a = q.get()
if a is None: return # Poison pill
print a
def thread2(threadname, q):
a = 0
for _ in xrange(10):
a += 1
q.put(a)
time.sleep(1)
q.put(None) # Poison pill
queue = Queue()
thread1 = Thread( target=thread1, args=("Thread-1", queue) )
thread2 = Thread( target=thread2, args=("Thread-2", queue) )
thread1.start()
thread2.start()
thread1.join()
thread2.join()
Kunci yang harus dipertimbangkan untuk digunakan, seperti threading.Lock
. Lihat lock-benda untuk info lebih lanjut.
Jawaban yang diterima DAPAT mencetak 10 oleh thread1, yang adalah bukan apa yang anda inginkan. Anda dapat menjalankan kode berikut untuk memahami bug lebih mudah.
def thread1(threadname):
while True:
if a % 2 and not a % 2:
print "unreachable."
def thread2(threadname):
global a
while True:
a += 1
Menggunakan kunci yang bisa melarang berubah dari a
sambil membaca lebih dari satu kali:
def thread1(threadname):
while True:
lock_a.acquire()
if a % 2 and not a % 2:
print "unreachable."
lock_a.release()
def thread2(threadname):
global a
while True:
lock_a.acquire()
a += 1
lock_a.release()
Jika benang menggunakan variabel untuk waktu yang lama, mengatasi ke variabel lokal pertama adalah pilihan yang baik.
Terima kasih banyak Jason Pan untuk menunjukkan bahwa metode. Yang thread1 jika pernyataan ini tidak atom, sehingga sementara pernyataan yang dijalankan, it's mungkin untuk thread2 untuk mengganggu thread1, memungkinkan non-dicapai kode untuk dicapai. I've diselenggarakan ide-ide dari sebelum posting ke program demonstrasi (di bawah ini) bahwa saya berlari dengan Python 2.7.
Dengan beberapa pemikiran analisis I'm yakin kita bisa mendapatkan wawasan lebih lanjut, tapi untuk sekarang saya pikir itu's penting untuk menunjukkan apa yang terjadi ketika non-perilaku atom memenuhi threading.
# ThreadTest01.py - Demonstrates that if non-atomic actions on
# global variables are protected, task can intrude on each other.
from threading import Thread
import time
# global variable
a = 0; NN = 100
def thread1(threadname):
while True:
if a % 2 and not a % 2:
print("unreachable.")
# end of thread1
def thread2(threadname):
global a
for _ in range(NN):
a += 1
time.sleep(0.1)
# end of thread2
thread1 = Thread(target=thread1, args=("Thread1",))
thread2 = Thread(target=thread2, args=("Thread2",))
thread1.start()
thread2.start()
thread2.join()
# end of ThreadTest01.py
Seperti yang diperkirakan, dalam menjalankan contoh, "terjangkau" kadang-kadang kode ini benar-benar tercapai, memproduksi output.
Hanya untuk menambahkan, ketika saya memasukkan kunci memperoleh/rilis pasangan ke thread1 saya menemukan bahwa kemungkinan memiliki "terjangkau" pesan cetak sangat berkurang. Untuk melihat pesan yang saya mengurangi waktu tidur untuk 0.01 sec dan peningkatan NN ke 1000.
Dengan kunci memperoleh/rilis pasangan di thread1 saya didn't berharap untuk melihat pesan sama sekali, tapi itu's ada. Setelah aku dimasukkan kunci memperoleh/rilis pasangan juga ke thread2, pesan tidak lagi muncul. Di belakang rambu, kenaikan pernyataan di thread2 mungkin juga non-atom.
Nah, berjalan contoh:
PERINGATAN! JANGAN PERNAH LAKUKAN INI DI RUMAH/BEKERJA! Hanya di dalam kelas ;)
Menggunakan semaphore, variabel bersama, dll. untuk menghindari kondisi terburu-buru.
from threading import Thread
import time
a = 0 # global variable
def thread1(threadname):
global a
for k in range(100):
print("{} {}".format(threadname, a))
time.sleep(0.1)
if k == 5:
a += 100
def thread2(threadname):
global a
for k in range(10):
a += 1
time.sleep(0.2)
thread1 = Thread(target=thread1, args=("Thread-1",))
thread2 = Thread(target=thread2, args=("Thread-2",))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
dan output:
Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 2
Thread-1 3
Thread-1 3
Thread-1 104
Thread-1 104
Thread-1 105
Thread-1 105
Thread-1 106
Thread-1 106
Thread-1 107
Thread-1 107
Thread-1 108
Thread-1 108
Thread-1 109
Thread-1 109
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Jika waktunya tepat, a += 100
operasi yang akan dilewati:
Prosesor mengeksekusi pada T a+100
dan mendapat 104. Tapi itu berhenti, dan melompat ke thread berikutnya
Di sini, Pada T+1 mengeksekusi +1
dengan nilai a, a == 4
. Jadi menghitung 5.
Melompat kembali (pada T+2), benang 1, dan menulis a=104
dalam memori.
Sekarang kembali pada benang 2, waktu adalah T+3 dan menulis a=5
dalam memori.
Voila! Berikutnya cetak instruksi ini akan cetak 5 bukan dari 104.
SANGAT bug jahat untuk direproduksi dan tertangkap.