2013-08-30 3 views
9

मेरे पास एकाधिक कार्यकर्ता धागे हैं, और एक जावाएफएक्स जीयूआई जो इन धागे में क्या हो रहा है पर रिपोर्ट कर रहा है।जावाएफएक्स में जटिल समेकन: एकाधिक कार्यकर्ता धागे से पर्यवेक्षण सूची और गुणों का उपयोग

धागे के बीच साझा बहुत सारे डेटा हैं और इसे देखने की आवश्यकता है। तो मैं JavaFX में डेटा को आसानी से दिखाने में सक्षम होने के लिए ऑब्जर्जेबललिस्ट और प्रॉपर्टी का उपयोग कर रहा हूं।

मैंने अपने आवेदन में क्या होता है इसके समान कुछ दिखाने के लिए एक छोटा सा उदाहरण ऐप बनाया है। इसमें 2 सूचियां हैं और कार्यकर्ता थ्रेड डेटा को एक सूची से दूसरे में ले जाता है। एक स्टेटस स्ट्रिंग को अद्यतित रखा जाता है। पूर्ण उदाहरण कोड http://codetidy.com/6569/ में पाया जा सकता है (इस कोड, दुर्घटना होगा बाद में देखें)

यहाँ साझा ObservableList के & गुण हैं:

private ObservableList<String> newItems; 
private ObservableList<String> readyItems; 
private StringProperty status; 

यहां बताया गया है कि वे JavaFX में किया जाता है:

listViewA.setItems(processor.getNewItems()); 
listViewB.setItems(processor.getReadyItems()); 
statusLabel.textProperty().bind(processor.getStatus()); 

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

Platform.runLater(new Runnable() { 
    @Override 
    public void run() { 
     synchronized (newItems) { 
      if (newItems.size() >= 5) 
       status.set("Overload"); 
      else 
       status.set("OK"); 
     } 
    } 
}); 

कि के लिए ठीक है:

Runnable newItemAdder = new Runnable() { 
     @Override 
     public void run() { 
      while(true) { 
       synchronized (newItems) { 
        String newItem = checkForNewItem(); //slow 
        if (newItem != null) { 
         newItems.add(newItem); 
         newItems.notify(); 
        } 
        if (newItems.size() >= 5) 
         status.set("Overload"); 
        else 
         status.set("OK"); 
       } 

       synchronized (readyItems) { 
        if (readyItems.size() > 10) 
         readyItems.remove(0); 
       } 

       try { Thread.sleep(200); } catch (InterruptedException e) { return; } 
      } 
     } 
    }; 
    new Thread(newItemAdder).start(); 

    Runnable worker = new Runnable() { 
     @Override 
     public void run() { 
      while(true) { 
       List<String> toProcess = new ArrayList<String>(); 
       synchronized (newItems) { 
        if (newItems.isEmpty()) 
         try { newItems.wait(); } catch (InterruptedException e) { return; } 
        toProcess.addAll(newItems); 
       } 

       for (String item : toProcess) { 
        String processedItem = processItem(item); //slow 
        synchronized (readyItems) { 
         readyItems.add(processedItem); 
        } 
       } 
      } 
     } 
    }; 
    new Thread(worker).start(); 

जरुर, कुछ बातें Platform.runLater साथ हल करने के लिए आसान कर रहे हैं: यह अगर मैं JavaFX धागे पर अद्यतन करने के लिए नहीं था कोड होगा गुण/सूचियां जिन्हें मैं केवल कार्य में लिखता हूं, और केवल जावाएफएक्स जीयूआई में पढ़ता हूं। लेकिन इस उदाहरण में सूचियों के लिए यह बहुत जटिल हो जाता है, जिस पर आपको सिंक्रनाइज़ करने, पढ़ने और लिखने की आवश्यकता होती है। आपको बहुत सारे Platform.runLater जोड़ने की आवश्यकता है और आपको "runLater" कार्य समाप्त होने तक अवरुद्ध करने की आवश्यकता है। इसका परिणाम कोड को पढ़ने और लिखने के लिए बहुत जटिल और कठिन होता है (मैं इस उदाहरण को इस तरह से चलाने में कामयाब रहा हूं, देखें कि मेरा क्या मतलब है: http://codetidy.com/6570/)।

क्या मेरे उदाहरण को काम करने के कोई अन्य तरीके हैं? मैं किसी अन्य समाधान या आंशिक समाधान की सराहना करेंगे ...

+0

आपने अपने कोड में क्या बदलाव किया है? क्या आप उन्हें दिखा सकते हैं?गहनेसा का आखिरी लिंक – AloneInTheDark

+0

@AloneInTheDark पर काम नहीं करता है, लेकिन मुझे डर है कि मेरे पास मूल उदाहरण कोड नहीं है, न ही गहने द्वारा संशोधित संस्करण। मैंने समाधान के सारांश के साथ एक अतिरिक्त उत्तर पोस्ट किया है। अच्छी सामान्य सलाह के लिए –

उत्तर

14

पृष्ठभूमि जानकारी

Task javadoc JavaFX में धागे के बीच डेटा पारित करने के लिए कई संगामिति उपयोग प्रतिमानों भी शामिल है।

Task में updateMessage जैसी सुविधा डेटा स्थानांतरण विधियां शामिल हैं और उपयोगकर्ता परिभाषित स्थिति संपत्ति के साथ आपके रननेबल के बजाय इसका उपयोग किया जा सकता है।

उपयुक्त होने पर, BlockingQueue जैसे समेकन के लिए डिज़ाइन की गई संग्रह संरचना का उपयोग करने पर विचार करें। एक अतिरिक्त लाभ यह है कि अवरुद्ध करने के लिए आकार सीमाएं हो सकती हैं, जो आपको पसंद की तरह लगती हैं।

कुछ सामान्य सलाह

  1. जब एक से अधिक थ्रेड में परिवर्तनशील नमूदार आइटम का उपयोग कर बहुत सावधान रहें। अनजाने में उन अद्यतनों को ट्रिगर करना आसान है जो दौड़ की स्थिति में परिणामस्वरूप होते हैं, एप्लिकेशन थ्रेड और अन्य थ्रेडिंग समस्याओं से सक्रिय दृश्य ग्राफ़ के अपडेट होते हैं।
  2. जितना संभव हो सके, म्यूटेबल अवलोकन योग्य वस्तुओं के बजाय immutable data का उपयोग करें।
  3. JavaFX concurrency और java.util.concurrent पुस्तकालयों से कुछ उच्च स्तरीय उपयोगिताओं का लाभ उठाएं।
  4. स्पष्ट सिंक्रनाइज़ेशन से बचें और यथासंभव बयान अधिसूचित करें।
  5. जावाफ़ैक्स एप्लिकेशन थ्रेड पर चलने वाले कोड में सिंक्रनाइज़ेशन या संभावित रूप से अवरुद्ध बयान रखने से सावधान रहें - क्योंकि आप अपना जीयूआई उत्तरदायी बना सकते हैं।
  6. केवल JavaFX समरूपता उपयोगिता का उपयोग करें जब आपको JavaFX थ्रेड के साथ बातचीत की आवश्यकता हो।
  7. मानक जावा concurrency उपयोगिताओं का उपयोग कर जावाएफएक्स थ्रेड के बहुत जटिल जटिल थ्रेडेड प्रोसेसिंग बंद करें। UI प्रतिक्रिया के समन्वय और नियंत्रण के लिए एक एकल समन्वय जावाएफएक्स कार्य करें।

उपर्युक्त अंगूठे के नियम हैं और व्यावहारिक रूप से पालन करने की आवश्यकता नहीं है।

यथोचित परिसर थ्रेडिंग नमूने

  • एक chart renderer यह दर्शाता है कि सिद्धांतों में से कुछ ऊपर 300 चार्ट प्रस्तुत करना, जबकि अभी भी यूआई अद्यतन और उपयोगकर्ता बातचीत प्रगति के लिए उत्तरदायी रखते हुए।
+1

+1। JavaFX बीन स्टाइल पैटर के अनुरूप जावाएफएक्स में जीयूआई तत्वों की आवश्यकता के लिए आवश्यक है कि वे हमेशा उत्परिवर्तनीय हों। इसके अलावा, जावाएफएक्स में नियंत्रक वर्ग हमेशा सार्वजनिक होना चाहिए। उत्परिवर्तन की आवश्यकता, encapsulation की हानि, और आवश्यकता है कि जीयूआई तत्व केवल एफएक्स आवेदन धागा पर संशोधित किया जा सकता है अत्यधिक समवर्ती क्षुधा के लिए एक बहुत ही विरोधी वातावरण के लिए बनाता है। – scottb

+0

आप समाधान वास्तविक डेटा और जीयूआई में दिखाए गए डेटा को विभाजित करते हैं, और एक थ्रेड जोड़ता है जो एक से दूसरे में अपडेट भेजता है। यह अतिरिक्त काम लगता है, लेकिन मुझे लगता है कि यह इस तरह की चीज को लागू करने का सबसे साफ तरीका है। यह सब ** बहुत ** कम जटिल बनाता है। मुझे आपके द्वारा उपयोग की जाने वाली ब्लॉकिंग क्यूई आदि भी पसंद है, यह कोड को बहुत आसान बनाता है। मुझे इस वर्ग के बारे में पता नहीं था, मुझे यह जांचना होगा कि java.util.concurrent में कौन सी उपयोगी चीजें हैं ... इस उत्कृष्ट उत्तर के लिए बहुत बहुत धन्यवाद! –

+0

मैंने अपने असली एप्लिकेशन में बदलाव पूरे कर लिए हैं। इसे मूल रूप से जावाएफएक्स के बिना मल्टीथ्रेड कोड में विभाजित किया गया है और एक धागा जो समय-समय पर जीयूआई अपडेट करता है (अच्छी तरह से सीधे जीयूआई नहीं, यह गुणों और पर्यवेक्षण सूची आदि के साथ "जावाएफएक्स स्टाइल मॉडल" अपडेट करता है)। वह हिस्सा जो "बहु-थ्रेडेड मॉडल" से समय-समय पर "जावाएफ़एक्स स्टाइल मॉडल" अपडेट करता है, कोड की 300 से अधिक पंक्तियां हैं (क्योंकि बहुत सारे डेटा दिखाना आवश्यक है)। यह बहुत अधिक कोड है, लेकिन यह आसान, पठनीय कोड है, और बहु-थ्रेडेड कोड अब उससे बहुत कम जटिल है। तो मैं इस समाधान से खुश हूं। –

1

पूर्ण उदाहरण के साथ मूल लिंक और गहने द्वारा उदाहरण समाधान मृत हैं, इसलिए मैं जो कुछ भी समाप्त कर रहा हूं उसके संक्षिप्त सारांश के साथ एक उत्तर जोड़ूंगा।

यह आसान बनाने के लिए, मान लीजिए कि आप 1 कक्षा से शुरू करते हैं जिसमें आपका डेटा मॉडल है (चलो इसे DataModel पर कॉल करें)। इस वर्ग का एक उदाहरण कई धागे द्वारा उपयोग किया जाता है जो इसे बदलता है।

अब समस्या यह है कि आप JavaFX में डेटा मॉडल का उपयोग करना चाहते है, लेकिन आप बस Property और ObservableList आदि का उपयोग करने के लिए अपने डेटा मॉडल नहीं बदल सकते आपको लगता है कि, श्रोताओं गैर JavaFX धागे से बुलाया जाएगा करते हैं, और जीयूआई उनके लिए बाध्य अपवाद फेंक देगा।

इसके बजाय आपको javaFX के लिए एक अलग डेटा मॉडल क्लास बनाने की आवश्यकता है। यह मूल रूप से जावाएफएक्स संस्करण है (चलिए इसे FXDataModel कहते हैं)। इसलिए इस संस्करण में एक ही जानकारी है, लेकिन यह जावाएफएक्स Property और ObservableList का उपयोग करती है। इस तरह, आप इसे अपने जीयूआई से बांध सकते हैं।

अगला चरण DataModel उदाहरण का उपयोग करके समय-समय पर FXDataModel उदाहरण अपडेट करना है। ऐसा करने के लिए, आप update(DataModel dataModel) विधि FXDataModel पर जोड़ें, जो मूल डेटा मॉडल से डेटा को FXDataModel उदाहरण में कॉपी करता है। यह अद्यतन फ़ंक्शन हमेशा जावाएफ़एक्स थ्रेड पर कॉल किया जाना चाहिए। आखिरकार, आपको केवल उस अपडेट फ़ंक्शन को कॉल करने की आवश्यकता है।

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

संबंधित मुद्दे