2013-01-05 116 views
3

我已經寫了一個使用線程的python tkinter代碼,以便tkinter嚮導通過在主線程中運行的tkinter mainloop自動更新,並在單獨的線程中運行後臺進程。但是我注意到,在運行代碼的一段時間後,python崩潰了。此外,它的本質是隨機的,但大多數時候蟒蛇崩潰。我寫了一個小的測試代碼,可以顯示這個問題(我的原始代碼與此類似,但具有一些實際進程和許多其他功能,所以我共享測試代碼)。python tkinter帶線程導致崩潰

###################################################################### 
# Test Code for Tkinter with threads 
import Tkinter 
import threading 
import Queue 
import time 

# Data Generator which will generate Data 
def GenerateData(q): 
    for i in range(1000000): 
     #print "Generating Some Data, Iteration %s" %(i) 
     time.sleep(0.01) 
     q.put("Some Data from iteration %s. Putting this data in the queue for testing" %(i)) 

# Queue which will be used for storing Data 
q = Queue.Queue() 

def QueueHandler(widinst, q): 
    linecount = 0 
    while True: 
     print "Running" 
     if not q.empty(): 
      str = q.get() 
      linecount = linecount + 1 
      widinst.configure(state="normal") 
      str = str + "\n" 
      widinst.insert("end", str) 
      if linecount > 100: 
       widinst.delete('1.0', '2.0') 
       linecount = linecount - 1 
      widinst.see('end') 
      widinst.configure(state="disabled") 

# Create a thread and run GUI & QueueHadnler in it 
tk = Tkinter.Tk() 
scrollbar = Tkinter.Scrollbar(tk) 
scrollbar.pack(side='right', fill='y') 
text_wid = Tkinter.Text(tk,yscrollcommand=scrollbar.set) 
text_wid.pack() 
t1 = threading.Thread(target=GenerateData, args=(q,)) 
t2 = threading.Thread(target=QueueHandler, args=(text_wid,q)) 
t2.start() 
t1.start() 

tk.mainloop() 
###################################################################### 

重現:

如果在IDLE打開此代碼並運行它,它有時似乎是在掛起狀態。所以要重現,請將睡眠時間從0.01修改爲0.1並運行。在停止應用程序並將其修改回0.01後,請保存並運行它。這一次它會運行,一段時間後,python將停止工作。我正在使用Windows 7(64位)。

問題

我已經提交給蟒蛇錯誤和它得到了拒絕。但我從一個stackoverflow問題中得到了這個想法,使用隊列在tkinter中寫入。有人可以建議應該做些什麼來處理它。

編輯的代碼:

# Test Code for Tkinter with threads 
import Tkinter 
import threading 
import Queue 
import time 

# Data Generator which will generate Data 
def GenerateData(q): 
    for i in range(1000000): 
     #print "Generating Some Data, Iteration %s" %(i) 
     time.sleep(0) 
     q.put("Some Data from iteration %s. Putting this data in the queue for testing" %(i)) 

# Queue which will be used for storing Data 
q = Queue.Queue() 

def QueueHandler(): 
    global widinst, q 
    linecount = 0 
    if not q.empty(): 
     str = q.get() 
     linecount = linecount + 1 
     widinst.configure(state="normal") 
     str = str + "\n" 
     widinst.insert("end", str) 
     if linecount > 100: 
      widinst.delete('1.0', '2.0') 
      linecount = linecount - 1 
     widinst.see('end') 
     widinst.configure(state="disabled") 
     tk.after(1,QueueHandler) 

# Create a thread and run GUI & QueueHadnler in it 
tk = Tkinter.Tk() 
scrollbar = Tkinter.Scrollbar(tk) 
scrollbar.pack(side='right', fill='y') 
text_wid = Tkinter.Text(tk,yscrollcommand=scrollbar.set) 
text_wid.pack() 
t1 = threading.Thread(target=GenerateData, args=(q,)) 
#t2 = threading.Thread(target=QueueHandler, args=(text_wid,q)) 
#t2.start() 
widinst = text_wid 
t1.start() 
tk.after(1,QueueHandler) 
tk.mainloop() 
+0

這個想法或者不同,或者它只是錯誤的。有關線程和GUI的問題不僅限於Tk,我知道大多數GUI工具包都會施加相同的限制:僅在GUI正在運行的線程中更新GUI。要更正您的示例,請將隊列讀取以及GUI更新移動到主線程。 'GenerateDate'繼續在他自己的線程上運行。 – mmgp

+0

更新的代碼是有問題的,因爲現在你的主線程沒有讓工作線程運行的機會。我看到你在worker線程中放了一個'time.sleep(0)',這使得它可以控制另一個線程(在這種情況下是主線程)。現在,在您的主線程中,您正在調用'tk.after(1,...)',該函數在任何時候都不會將控制權返回給其他線程。所以,你所要做的就是在那裏也添加'time.sleep(0)'。現在兩個線程都很好地運行,允許彼此運行。 – mmgp

+0

@mmgp - 感謝您指出。 – sarbjit

回答

5

的Tkinter不是線程安全的;除了主線程外,您無法從任何地方訪問Tkinter窗口小部件。您需要重構代碼,以便QueueHandler在主線程中運行。

+0

明白了,它現在的工作。謝謝 – sarbjit

+0

我現在看到的還有一個問題: - 在我的代碼中,我使用'after'來調用QueueHandler來更新GUI。但是,如果我從'GenerateData'線程中取消打印語句的註釋,則只能看到控制檯上的打印語句,並且GUI從不更新。只要我將該打印語句註釋回來,GUI就會開始填充。有一次我可以看到打印的報表和GUI更新。這似乎是隨機的??。您能否對此行爲發表評論 – sarbjit

+0

我發佈了導致此問題的更新代碼 – sarbjit