2016-08-02 4 views
8

मैं क्यूटी में एक बहुप्रचारित आवेदन लिख रहा हूं (अपने स्वयं के ईवेंट लूप के साथ कई धागे)। लॉगिंग करते समय मैं लॉगजर को लॉग में एक थ्रेड आईडी (उनके सार्थक नाम) शामिल करना चाहता हूं। क्यूटी डिफ़ॉल्ट लॉगर ऐसा करने में असमर्थ लगता है। तो मैं तीन विकल्प हैं:एक लॉगर के लिए एक अलग धागा बना रहा है ठीक है?

  1. हर धागा अपने आप में प्रवेश करने देता है (इस mutexes शामिल है, इसलिए शायद सबसे खराब दृष्टिकोण है, लेकिन मुझे यकीन है कि नहीं कर रहा हूँ)
  2. एक समर्पित लकड़हारा धागा और अन्य नहीं है धागे सीधे 2. के रूप में (शायद तेजी से 3. से) घटनाओं पोस्ट इसे में
  3. ही लेकिन संदेश संकेत/स्लॉट प्रणाली के माध्यम से भेजा जाता है (वास्तव में, यह एक घटना के रूप में अच्छी पोस्टिंग में परिणाम होगा) ।

कौन सा बेहतर है और सामान्य रूप से इसे करने के सर्वोत्तम अभ्यास क्या हैं?


कुछ बातें टिप्पणी में सवाल के बाद स्पष्ट करने के लिए:

  • QThread एक मानक पद्धति postEvent() है, कि धागे-सुरक्षित है।

तो सवाल बन जाता है, लकड़हारा धागा कतार

  • किसी प्रकार का सार है कि भर में घटना के डेटा प्रमुखता की लागत का औचित्य साबित करने घटना प्रति काफी काम करने की आवश्यकता है सवाल का मुझे पता है कि सबसे अच्छा जवाब "उपाय!" है, लेकिन वर्तमान में ऐप प्रारंभिक विकास चरण में है, मापने के लिए बहुत कुछ नहीं है। शुरुआत से ही सही डिजाइन चुनना हमेशा अच्छा होता है।
  • मेरे मामले में धागे शायद एक अच्छा विचार है: यह एक मीडिया प्लेयर है, इसलिए जीयूआई थ्रेड, प्लेबैक थ्रेड, डीबी/मीडिया लाइब्रेरी थ्रेड, नेटवर्क थ्रेड पूल ... दूसरे शब्दों में धागे का पूरा चिड़ियाघर है।
+1

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

+0

आपको शायद एक समर्पित लॉगिंग थ्रेड के लिए सिंक्रनाइज़ेशन की आवश्यकता होगी (अंतर्निहित हो सकता है, लेकिन अन्य धागे की घटनाओं या पोस्ट कहीं भी कतारबद्ध हैं, जिन्हें सिंक्रनाइज़ किया जाना चाहिए)। –

+0

@JesperJuhl गैर-समानांतर लॉगिंग रन टाइम व्यवहार को बदलता है, शायद बहुत अधिक (उदाहरण के लिए जब उच्च व्यवहार स्तर कुछ व्यवहार को डिबग करने के लिए चालू होता है जो तब गायब हो जाता है)। क्योंकि एक समर्पित लॉगिंग थ्रेड के बिना आपको लॉग इन एंट्री लिखते समय * अन्य थ्रेड को पूरे समय लॉगिंग से अवरुद्ध करना होगा *, या गन्दा इंटरमीस्ड लॉग स्वीकार करना होगा, जो जल्दी बेकार हो जाता है। –

उत्तर

8

ये सामान्य टिप्पणियां हैं क्योंकि मुझे क्यूटी के साथ कोई अनुभव नहीं है। कतार की लागत के संबंध में, सामान्य रूप से: I/O आमतौर पर अन्य रन टाइम लागतों को कम करने देता है, इसलिए इससे कोई फर्क नहीं पड़ता।

एक समर्पित प्रवेश धागे के गुण:

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

(डी) प्रत्येक धागे से सीधे लॉगिंग के फायदे ऊपर के विपरीत हैं। कोई भी दो धागे एक ही समय में लॉग इन नहीं कर सकते हैं (या तो क्योंकि printf() जैसे फ़ंक्शन FILE को तत्काल लॉक करते हैं, या क्योंकि आप लॉग फ़ंक्शन को सिंक्रनाइज़ करते हैं); यह सभी धागे बनाता है जो मौजूदा धागे होने तक ब्लॉक लॉग करना चाहते हैं। यदि डिबगिंग उद्देश्यों के लिए लॉगिंग की जाती है तो कोई भी अनबफर किया जाना चाह सकता है (ताकि बाद में क्रैश होने की स्थिति में डेटा खो न जाए), जो रन टाइम प्रभाव को बढ़ाता है।

यह कितना बुरा है कि आवेदन की प्रकृति के साथ-साथ लॉगिंग तंत्र और डेटा की मात्रा पर निर्भर करता है।

+0

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

2

मैंने का उपयोग करके एक अच्छा साफ तरीके से क्यूटी अनुप्रयोगों के लिए लॉगिंग तंत्र लागू किए हैं।

एक क्यूटी अनुप्रयोग में आवेदन का प्रतिनिधित्व करने वाले QApplication का एक उदाहरण है।

आप QEvent से विरासत में अपनी खुद की घटनाएं बना सकते हैं, और उन्हें पोस्ट कर सकते हैं और एप्लिकेशन के लिए QApplication ऑब्जेक्ट का उपयोग करके उन्हें संभाल सकते हैं।

इसलिए उदाहरण के लिए आप अपने लॉग घटना वर्ग

MyLogEvent : public QEvent 
{ 
public: 
    MyLogEvent(QString threadId, QString logMessage) : QEvent(QEvent::User) 
     { /* Store the ThreadID and log message, with accessor functions */}; 
} 

हो सकता है और आप

MyLogEvent *event = new MyLogEvent(QString("Thread 1"), QString("Something Happened")); 
QApplication::postEvent(mainWindow, event); 

उपयोग कर किसी भी क्यूटी धागे से घटनाओं पोस्ट कर सकते हैं हैंडलर एक मुख्य विंडो वस्तु हो सकता है (अगर आप चाहते हैं विंडो पर लॉग इन करने के लिए), या यदि आप उदाहरण देना चाहते हैं तो एक समर्पित ऑब्जेक्ट एक फ़ाइल में लॉग इन करें।

वस्तु घटनाओं से निपटने में, लॉग संदेशों

bool MainWindow::event(QEvent *e) 
{ 
    if(e->type()==QEvent::User) 
    { 
     // This is a log event 
     MyLogEvent *logEvent = static_cast<MyLogEvent *>(e); 
     ui.textEdit->appendPlainText(logEvent->logMessage()) 
     return true; 
    } 
    return QMainWindow::event(e); 
} 
1

मैं काफी क्यों अपने आप में प्रवेश कर रही है हर धागा एक स्पष्ट म्युटेक्स उनका उपयोग करना होगा समझ में नहीं आता संभाल करने QObject :: घटना को ओवरराइड।

यदि आप डिस्क फ़ाइल में लॉग इन कर रहे हैं, तो प्रत्येक थ्रेड अपनी फ़ाइल में लॉगिंग कर सकता है। आप एक आम उपसर्ग के साथ फ़ाइलों का नाम कर सकते हैं:

QFile * logFile(QObject * parent = nullptr) { 
    auto baseName = QStringLiteral("MyApplication-"); 
    auto threadName = QThread::currentThread()->objectName(); 
    if (threadName.isEmpty()) 
    return new QTemporaryFile(baseName); 
    else 
    return new QFile(baseName + threadName); 
} 

ऑपरेटिंग सिस्टम इसका फ़ाइल सिस्टम म्युटेक्स (ते) के माध्यम से पहुंच serializing कर रहा है।

यदि आप एक ऐसे डेटाबेस में लॉग इन कर रहे हैं जो समवर्ती पहुंच का समर्थन करता है, जैसे कि उचित समेकन विकल्प के साथ एसक्लाइट चयनित है, तो डेटाबेस ड्राइवर सीरियलाइजिंग एक्सेस का ख्याल रखेगा।

यदि आप एक सामान्य धागे पर लॉग इन कर रहे हैं, तो ईवेंट कतार में एक म्यूटेक्स होता है जिसे आप स्वचालित रूप से postEvent के साथ क्रमबद्ध करते हैं।

आप सही हैं कि सिग्नल-स्लॉट तंत्र का उपयोग करके आप सीधे घटनाओं का उपयोग करके अधिक खरीद नहीं लेते हैं। वास्तव में, यह अधिक स्मृति आवंटन करने की गारंटी है, इसलिए आपको स्वयं को एक ईवेंट पोस्ट करना पसंद करना चाहिए, आदर्श रूप से एक ऐसा ईवेंट जो QVarLengthArray<char> का उपयोग करता है जो लॉग संदेशों के "सबसे अधिक" फिट बैठता है।फिर, आवंटन इस तरह की घटना के लिए एक एकल malloc कॉल के साथ किया जाता है:

// logger.h 

struct MyLogEvent : QEvent { 
    constexpr static QEvent::Type theType() { return (QEvent::Type)(QEvent::User + 1); } 
    QVarLengthArray<char, 128> message; 
    MyLogEvent(const char * msg) : QEvent(theType()) { 
    message.append(msg, strlen(msg)); 
    } 
}; 

class Logger : public QObject { 
    ... 
public: 
    static void log(const char * msg) { 
    QCoreApplication::postEvent(instance(), new MyLogEvent(msg)); 
    } 
    static Logger * instance(); // singleton, must be a thread safe method 
}; 

// logger.cpp 
... 
Q_GLOBAL_STATIC(Logger, loggerInstance); 

Logger * Logger::instance() { 
    // Thread-safe since QGlobalStatic is. 
    return loggerInstance; 
} 

था आप एक QByteArray या एक QString इस्तेमाल किया, अभिव्यक्ति new MyLogEvent कम से कम दो आवंटन प्रदर्शन किया जाएगा।

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