2012-06-29 10 views
11

मैं अपने पायसाइड जीयूआई अनुप्रयोग में काफी आम बात करने की कोशिश कर रहा हूं: मैं कुछ सीपीयू-गहन कार्य को पृष्ठभूमि धागे में भेजना चाहता हूं ताकि मेरा जीयूआई रहता है उत्तरदायी और गणना के रूप में एक प्रगति संकेतक भी प्रदर्शित कर सकता है।पायसाइड/पीईक्यूटी - एक सीपीयू गहन धागा शुरू करना पूरे आवेदन को लटकता है

यहाँ मैं क्या कर रहा हूं (मैं PySide 1.1.1 का उपयोग कर रहा अजगर 2.7, लिनक्स x86_64 पर) है:

import sys 
import time 
from PySide.QtGui import QMainWindow, QPushButton, QApplication, QWidget 
from PySide.QtCore import QThread, QObject, Signal, Slot 

class Worker(QObject): 
    done_signal = Signal() 

    def __init__(self, parent = None): 
     QObject.__init__(self, parent) 

    @Slot() 
    def do_stuff(self): 
     print "[thread %x] computation started" % self.thread().currentThreadId() 
     for i in range(30): 
      # time.sleep(0.2) 
      x = 1000000 
      y = 100**x 
     print "[thread %x] computation ended" % self.thread().currentThreadId() 
     self.done_signal.emit() 


class Example(QWidget): 

    def __init__(self): 
     super(Example, self).__init__() 

     self.initUI() 

     self.work_thread = QThread() 
     self.worker = Worker() 
     self.worker.moveToThread(self.work_thread) 
     self.work_thread.started.connect(self.worker.do_stuff) 
     self.worker.done_signal.connect(self.work_done) 

    def initUI(self): 

     self.btn = QPushButton('Do stuff', self) 
     self.btn.resize(self.btn.sizeHint()) 
     self.btn.move(50, 50)  
     self.btn.clicked.connect(self.execute_thread) 

     self.setGeometry(300, 300, 250, 150) 
     self.setWindowTitle('Test')  
     self.show() 


    def execute_thread(self): 
     self.btn.setEnabled(False) 
     self.btn.setText('Waiting...') 
     self.work_thread.start() 
     print "[main %x] started" % (self.thread().currentThreadId()) 

    def work_done(self): 
     self.btn.setText('Do stuff') 
     self.btn.setEnabled(True) 
     self.work_thread.exit() 
     print "[main %x] ended" % (self.thread().currentThreadId()) 


def main(): 

    app = QApplication(sys.argv) 
    ex = Example() 
    sys.exit(app.exec_()) 


if __name__ == '__main__': 
    main() 

आवेदन एक बटन के साथ एक एकल खिड़की प्रदर्शित करता है। जब बटन दबाया जाता है, तो मैं गणना करता हूं कि गणना के दौरान इसे स्वयं अक्षम कर दें। फिर, बटन पुनः सक्षम होना चाहिए।

क्या होता है, इसके बजाए, जब मैं बटन दबाता हूं तो गणना पूरी होने पर पूरी विंडो फ्रीज होती है और फिर, जब यह समाप्त हो जाती है, तो मैं एप्लिकेशन पर नियंत्रण प्राप्त करता हूं। बटन कभी अक्षम नहीं प्रतीत होता है। मैंने देखा एक मजेदार बात यह है कि अगर मैं एक सरल समय के साथ do_stuff() में सीपीयू गहन गणना को प्रतिस्थापित करता हूं। नींद() कार्यक्रम अपेक्षित व्यवहार करता है।

मुझे नहीं पता कि क्या हो रहा है, लेकिन ऐसा लगता है कि दूसरी धागा की प्राथमिकता इतनी अधिक है कि यह वास्तव में निर्धारित होने से जीयूआई थ्रेड को रोक रहा है। यदि दूसरा धागा ब्लॉक किए गए राज्य में जाता है (जैसा कि यह sleep() के साथ होता है), तो जीयूआई वास्तव में अपेक्षित रूप से इंटरफ़ेस को चलाने और अपडेट करने का मौका देता है। मैंने कार्यकर्ता थ्रेड प्राथमिकता को बदलने की कोशिश की, लेकिन ऐसा लगता है कि यह लिनक्स पर नहीं किया जा सकता है।

इसके अलावा, मैं थ्रेड आईडी मुद्रित करने का प्रयास करता हूं, लेकिन मुझे यकीन नहीं है कि मैं इसे सही तरीके से कर रहा हूं। यदि मैं हूं, तो थ्रेड एफ़िनिटी सही प्रतीत होती है।

मैंने पीईक्यूटी के साथ कार्यक्रम की भी कोशिश की और व्यवहार बिल्कुल वही है, इसलिए टैग और शीर्षक। अगर मैं इसे पायसाइड के बजाय पीईक्यूटी 4 के साथ चला सकता हूं तो मैं अपने पूरे एप्लिकेशन को पीईक्यूटी 4

उत्तर

12

पर स्विच कर सकता हूं, यह शायद पाइथन के जीआईएल वाले कार्यकर्ता धागे के कारण होता है। कुछ पायथन कार्यान्वयन में, एक समय में केवल एक पायथन थ्रेड निष्पादित हो सकता है। जीआईएल अन्य धागे को पायथन कोड निष्पादित करने से रोकता है, और इसे फ़ंक्शन कॉल के दौरान जारी किया जाता है जिसे जीआईएल की आवश्यकता नहीं होती है।

उदाहरण के लिए, वास्तविक आईओ के दौरान जीआईएल जारी किया जाता है, क्योंकि आईओ को ऑपरेटिंग सिस्टम द्वारा नियंत्रित किया जाता है, न कि पाइथन दुभाषिया।

समाधान:

  1. जाहिर है, आप अपने कार्यकर्ता सूत्र में time.sleep(0) का उपयोग अन्य सूत्र (according to this SO question) करने के लिए उपज कर सकते हैं। आपको समय-समय पर time.sleep(0) पर कॉल करना होगा, और जीयूआई थ्रेड केवल तभी चलाएगा जब पृष्ठभूमि थ्रेड इस फ़ंक्शन को कॉल कर रहा हो।

  2. यदि कार्यकर्ता धागा पर्याप्त रूप से आत्मनिर्भर है, तो आप इसे पूरी तरह से अलग प्रक्रिया में डाल सकते हैं, और फिर पाइपों पर मसालेदार वस्तुओं को भेजकर संवाद कर सकते हैं। अग्रभूमि प्रक्रिया में, पृष्ठभूमि प्रक्रिया के साथ आईओ करने के लिए एक कार्यकर्ता थ्रेड बनाएं। चूंकि वर्कर थ्रेड सीपीयू ऑपरेशंस के बजाय आईओ कर रहा है, इसलिए यह जीआईएल नहीं रखेगा और यह आपको पूरी तरह उत्तरदायी जीयूआई थ्रेड देगा।

  3. कुछ पायथन कार्यान्वयन (जेपीथन और आयरनपीथन) में एक जीआईएल नहीं है।CPython में

धागे केवल वास्तव में पृष्ठभूमि में CPU- सघन कार्य डालने के लिए नहीं, बहुसंकेतन आईओ के संचालन के लिए उपयोगी होते हैं। कई अनुप्रयोगों के लिए, सीपीथन कार्यान्वयन में थ्रेडिंग मूल रूप से टूटा हुआ है और यह संभव भविष्य के लिए इस तरह रहने की संभावना है।

+0

मैं डाल 'time.sleep (0)' 'time.sleep के बजाय (0.2)' उदाहरण में टिप्पणी और, दुर्भाग्य से, समस्या हल नहीं होती कोशिश की। मैंने देखा कि 'time.sleep (0.05)' जैसे मानों के साथ बटन अपेक्षित व्यवहार करता है। एक कामकाज के रूप में, मैं कार्यकर्ता को प्रति सेकंड केवल कुछ बार प्रगति की रिपोर्ट करने के लिए स्थापित कर सकता हूं और जीयूआई अपडेट को स्वयं छोड़ने के लिए सो सकता हूं। – user1491306

+1

धागे और जीयूआई के लिए कामकाज हैं (जैसे कि http://code.activestate.com/recipes/578154)। –

+0

मैं इस तरह से समाप्त हो गया: मैंने 'नींद' लगाई जहां गणना शुरू होती है, और जब भी प्रगति संकेतक अद्यतन होता है तो मैं "abort" बटन काम करने के लिए 'app.processEvents()' को कॉल करता हूं। – user1491306

0

अंत में यह मेरी समस्या के लिए काम करता है - तो कोड किसी और की मदद कर सकता है।

import sys 
from PySide import QtCore, QtGui 
import time 

class qOB(QtCore.QObject): 

    send_data = QtCore.Signal(float, float) 

    def __init__(self, parent = None): 
     QtCore.QObject.__init__(self) 
     self.parent = None 
     self._emit_locked = 1 
     self._emit_mutex = QtCore.QMutex() 

    def get_emit_locked(self): 
     self._emit_mutex.lock() 
     value = self._emit_locked 
     self._emit_mutex.unlock() 
     return value 

    @QtCore.Slot(int) 
    def set_emit_locked(self, value): 
     self._emit_mutex.lock() 
     self._emit_locked = value 
     self._emit_mutex.unlock() 

    @QtCore.Slot() 
    def execute(self): 
     t2_z = 0 
     t1_z = 0 
     while True: 
      t = time.clock() 

      if self.get_emit_locked() == 1: # cleaner 
      #if self._emit_locked == 1: # seems a bit faster but less    responsive, t1 = 0.07, t2 = 150 
       self.set_emit_locked(0) 
       self.send_data.emit((t-t1_z)*1000, (t-t2_z)*1000) 
       t2_z = t 

      t1_z = t 

class window(QtGui.QMainWindow): 

    def __init__(self): 
     QtGui.QMainWindow.__init__(self) 

     self.l = QtGui.QLabel(self) 
     self.l.setText("eins") 

     self.l2 = QtGui.QLabel(self) 
     self.l2.setText("zwei") 

     self.l2.move(0, 20) 

     self.show() 

     self.q = qOB(self) 
     self.q.send_data.connect(self.setLabel) 

     self.t = QtCore.QThread() 
     self.t.started.connect(self.q.execute) 
     self.q.moveToThread(self.t) 

     self.t.start() 

    @QtCore.Slot(float, float) 
    def setLabel(self, inp1, inp2): 

     self.l.setText(str(inp1)) 
     self.l2.setText(str(inp2)) 

     self.q.set_emit_locked(1) 



if __name__ == '__main__': 

    app = QtGui.QApplication(sys.argv) 
    win = window() 
    sys.exit(app.exec_()) 
संबंधित मुद्दे