رفتن به مطلب
جامعه‌ی برنامه‌نویسان مُدرن ایران
  • 0
GornerLabo

مشکل استفاده صحیح از QTimer و QThread در Qt

سوال

با سلام خدمت اساتید محترم. اگر پروژه ای که در کیوت ساختیم رو بخواییم از کلاس های QThread و QTimer استفاده کنیم ، در چه بخش هایی از پروژه باید استفاده کنیم؟ با توجه به جستجویی که داشتم هیچ از یک منابع به طور کامل و جامع توضیح ندادند که در پروژه های کیوت در چه قسمت هایی باید از QThread استفاده شه و در چه قسمت هایی از QTimer ! هدف من از این سوال این هست که در حین انجام یک پروسس از کدام کلاس باید استفاده کرد که ui هنگ نکنه!

از اساتید محترم تقاضا دارم کامل این مشکل رو توضیح بدن با تشکر فراوان. 

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

5 پاسخ به این سوال تا کنون داده شده است

پست های پیشنهاد شده

  • 0
در 2 ساعت قبل، GornerLabo گفته است :

با سلام خدمت اساتید محترم. اگر پروژه ای که در کیوت ساختیم رو بخواییم از کلاس های QThread و QTimer استفاده کنیم ، در چه بخش هایی از پروژه باید استفاده کنیم؟ با توجه به جستجویی که داشتم هیچ از یک منابع به طور کامل و جامع توضیح ندادند که در پروژه های کیوت در چه قسمت هایی باید از QThread استفاده شه و در چه قسمت هایی از QTimer ! هدف من از این سوال این هست که در حین انجام یک پروسس از کدام کلاس باید استفاده کرد که ui هنگ نکنه!

از اساتید محترم تقاضا دارم کامل این مشکل رو توضیح بدن با تشکر فراوان. 

 کلاس های مربوط به thread ها در هر پلت فرمی جهت پردازش یک فرآیند در یک نخ (thread) دیگری به غیر از نخ اصلی برنامه، که این نخ می تواند در سطح کاربر باشه ویا در سطح کرنل باشه کاربرد دارند.

و البته اگر بخواهید بهتر متوجه عملکرد نخ ها شوید باید درباره ساختمان نخ ها و نحوه اجرای اونها در سیستم عامل های(سکوها) مختلف اینکه مثلا حوضچه های نخ اصولا چی هستند و یا اینکه نخ ها (STA (Single Thread Apartement  چه مکانیزمی دارند ویا مارشالینگ چطور اتفاق می افته ویا اینکه تعریف هایی مثل volatile , yield چه هستند و چطور استفاده می شوند ویا Race Condition چی هست ویا ناحیه بحرانی چطور اتفاق می افتند و خیلی موارد دیگه حتما باید کتاب هایی در این باره مطالعه کنید و البته تمرین زیادی کنید تا بتوانید این مطالب را بهتر متوجه شوید.

در کیوت یک نخ اصلی (Main Thread) داریم که یکی از وظایفش ساخت متریالهای روی فرمهاست پس بنابراین اگر پروسه ای دارید که طولانی هست را اگر در این نخ اصلی اجرا کنید مطمنا برنامه freeze خواهدشد برای اینکه این اتفاق رخ نده شما این پردازش را تو یک نخ دیگه که اگر از کلاس QThread استفاده کنید در سطح کاربر ساخته می شود، باید اجرا کنید بنابراین از freeze شدن برنامه هم جلوگیری می شود.

البته اگر در استفاده از نخ ها نیازی به اطلاعات اشتراکی بین نخ ها یا حتی پروسس ها در حوضچه ها و یا اگر نیازی به مکانیسم های قفل گذاری و ... نداشته باشید خیلی هم پیچیده نخواهند بود و به سادگی قابل استفاده هستند. 

ودرباره کلاس Qtimer هم دقیقا یک سطح انتزاعی بالاتر از کلاس نخ هست که شما می توانیدفرآیند های تکرارگر خودتون رادر زمان های معینی اجرا نمایید.مثال چک کردن یک پروسه در back bone برنامه جهت دریافت یک مقدار از نت ... 

البته روش های بهینه تری هم هستند برای انجام ای کارها ولی اگر برنامه شما نیازی به پیچیدگی ندارد می توانید از این کلاسها استفاده نمایید.

در کل استفاده از نخ ها و پروسس ها و کلا مباحث مربوط به نخ ها نیاز به مطالعه مباحثی که در بالا اشاره کردم و همچنین کتابهایی در زمینه هسته سیستم عامل هم خیلی مفید خواهد بود.

به هر حال همیشه توجه داشته باشید که استفاده از نخ ها بدون دانش فنی کافی می تونه خیلی هزینه داشته باشه و حتی برخی موارد می تونه خیلی بدتر از زمانی باشه که از نخ استفاده نکرده باشید.

به این نکته هم توجه کنید که معماری سیستم عامل و همچنین معماری کامپایلر ها و همچنین معماری پلت فرم ها در نحوه استفاده از نخ ها خیلی تاثیر داره به طور واضح یعنی اینکه شما تو سیستم عامل ویندوز و کامپایلر ویژوال سی در دات نت نخ ها ملاحظات خاص خودشون را دارند حالا در لینوکس و کامپایلر gcc یک ملاحظه دارند و در سی شارپ دات نت در ویندوز ملاحظات فنی خاص خودشون در جاوا و virtual machine یک ملاحضه.

بنابراین با تعاریف گفته شده، نحوه استفاده از نخ ها به یک سکو ویا یک فریم وورکی مثل کیوت وابسته نیست!

سعی میکنم چندتا مثال ساده برای شما بزنم..

در این مثال سه نخ کاربری داریم دوتا توسط برنامه ایجاد شده اند و یک نخ هم که نخ اصلی برنامه می باشد در این برنامه با استفاده از کلاس thread_local یک متغیر لوکال تعریف شده است درنخ اصلی برنامه و بعد با استفاده از قفل mutex برای جلوگیری از data collision در نخ های ایجاد شده به متغیری که به صورت لوکال برای نخ اصلی تعریف شده است دسترسی خواهیم داشت و مقدار آن را برای همان نخ میزبان زیاد میکنیم و نمایش میدیم ولی همین متغیر در نخ اصلی برنامه چون تغییری نداشته است مقدار پیش فرض را نمایش میدهد.

هدف برنامه : استفاده از متغیر حافظه کلاس thread_local در 11++c.

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
 
thread_local unsigned int rage = 1; 
std::mutex cout_mutex;
 
void increase_rage(const std::string& thread_name)
{
    ++rage; // modifying outside a lock is okay; this is a thread-local variable
    std::lock_guard<std::mutex> lock(cout_mutex);
    std::cout << "Rage counter for " << thread_name << ": " << rage << '\n';
}
 
int main()
{
    std::thread a(increase_rage, "a"), b(increase_rage, "b");
 
    {
        std::lock_guard<std::mutex> lock(cout_mutex);
        std::cout << "Rage counter for main: " << rage << '\n';
    }
 
    a.join();
    b.join();
}

برای کیوت ...

class Thread : public QThread
{
    Q_OBJECT

public:
    Thread();

    void setMessage(const QString &message);
    void stop();

protected:
    void run();

private:
    QString messageStr;
    volatile bool stopped;
};

Thread::Thread()
{
    stopped = false;
}

void Thread::run()
{
    while (!stopped)
        std::cerr << qPrintable(messageStr);
    stopped = false;
    std::cerr << std::endl;
}

void Thread::stop()
{
    stopped = true;
}

و نحوه استفاده از این کلاس...

ThreadDialog::ThreadDialog(QWidget *parent)
    : QDialog(parent)
{
    threadA.setMessage("A");
    threadB.setMessage("B");

    threadAButton = new QPushButton(tr("Start A"));
    threadBButton = new QPushButton(tr("Start B"));
    quitButton = new QPushButton(tr("Quit"));
    quitButton->setDefault(true);

    connect(threadAButton, SIGNAL(clicked()),
            this, SLOT(startOrStopThreadA()));
    connect(threadBButton, SIGNAL(clicked()),
            this, SLOT(startOrStopThreadB()));
}

void ThreadDialog::startOrStopThreadA()
{
    if (threadA.isRunning()) {
        threadA.stop();
        threadAButton->setText(tr("Start A"));
    } else {
        threadA.start();
        threadAButton->setText(tr("Stop A"));
    }
}

void ThreadDialog::startOrStopThreadB()
{
    if (threadB.isRunning()) {
        threadB.stop();
        threadBButton->setText(tr("Start B"));
    } else {
        threadB.start();
        threadBButton->setText(tr("Stop B"));
    }
}

void ThreadDialog::closeEvent(QCloseEvent *event)
{
    threadA.stop();
    threadB.stop();
    threadA.wait();
    threadB.wait();
    event->accept();
}

 

  • پسندیدن 1
  • تشکر شده 2

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0

ممنون بابت توضیح کامل. استاد اگه پروسس سنگین باشه ( مثل عمل رایت یک فایل حجیم باینری) به همین شیوه که برای کیوت مثال زدین عمل کنیم؟ 

  • تشکر شده 1

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0
در 1 دقیقه قبل، GornerLabo گفته است :

ممنون بابت توضیح کامل. استاد

اول اینو بگم البته میدونم از روی حسن نیت اینو میگید! ولی این (استاد)، عنوان بزرگی هست از من حرفه ای تر هم تو این سایت هست همون آقا فرهاد باشه ما را بس.!

در 3 دقیقه قبل، GornerLabo گفته است :

اگه پروسس سنگین باشه ( مثل عمل رایت یک فایل حجیم باینری) به همین شیوه که برای کیوت مثال زدین عمل کنیم؟ 

بله می توانید با یک نخ در پس زمینه برنامه اصلی این عملیات را انجام بدید. اگر با همین روشی که در مثال ذکر شده یک کلاس wrapper برای نخ ها تدارک ببنید در آینده مدیریت اونها خیلی راحتتر خواهد بود بنابراین سعی کنید یک کلاس کلی برای نخ ها در برنامه هاتون ایجاد کنید که از الگوی Abstract factory استفاده کرده باشید.

فقط رعایت اصول ui / ux را فراموش نکنید یعنی حتما باید در یک قسمت مناسب از برنامه پیشرفت این عملیات را هر لحظه به کاربر نمایش بدید و این نکته بسیار مهم هست.

  • پسندیدن 1

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0

آقا فرهاد الان توی برنامه م یک تابع دارم که یه اسلاتی رو اجرا میکنه. تا جایی که اطلاع دارم اسلات میره تو ترد بعد اجرا. پس چرا وقتی این تابع اجرا میشه ( در اصل اسلات رو اجرا میکنه) تا اسلات کارش تموم نشده ، ui هنگ میکنه؟

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0
در 2 ساعت قبل، GornerLabo گفته است :

آقا فرهاد الان توی برنامه م یک تابع دارم که یه اسلاتی رو اجرا میکنه. تا جایی که اطلاع دارم اسلات میره تو ترد بعد اجرا. پس چرا وقتی این تابع اجرا میشه ( در اصل اسلات رو اجرا میکنه) تا اسلات کارش تموم نشده ، ui هنگ میکنه؟

بهتره که سورس کد تون را اینجا قرار می دادید!

ولی یک مثال میزنم...

در مثال زیر دوتا نخ داریم یک نخ اصلی و یک نخ کارگر ، بعد از شروع نخ کارگر یک سیگنال را از توی نخ اصلی ارسال میکنیم و نخ کارگر را به حالت انتظار می بریم اگر یکبار کدزیر را اجراکنید بهتر متوجه میشید.

پس نکته قابل ذکر این هست که شما باید نخ کارگر را با استفاده از این متد b.moveToThread(&wThread); به حوضچه نخ های QObject ارسال کنید اگر می خواهید از چند نخ استفاده کنید در کنار نخ اصلی برنامه.

البته معمولا استفاده از کتابخانه های همزمانی مانند QtConcurrent  خیلی بهینه تر مدیریت نخ ها را برای شما انجام می دهند.!

#include <QtCore>
struct myThread : QObject {
  Q_OBJECT

  Q_SLOT void aSlot() { 
    qDebug() << QThread::currentThread(); 
    QThread::currentThread()->quit();
  }

  Q_SIGNAL void aSignal();
};

int main(int argc, char ** argv) {
  QCoreApplication app{argc, argv};
  
  //set the name for main thread
  QThread::currentThread()->setObjectName("main");
  
  //create new worker thread
  QThread wThread;
  wThread.setObjectName("worker_thread");
  
  myThread a, b;
  b.moveToThread(&wThread);//move worker thread to QObject pool thread
  wThread.start();
  
  QObject::connect(&a, &myThread::aSignal, &b, &myThread::aSlot);
  emit a.aSignal(); // the signal is emitted from the main thread
  wThread.wait();
}

 

  • پسندیدن 2

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

برای ارسال دیدگاه یک حساب کاربری ایجاد کنید یا وارد حساب خود شوید

برای اینکه بتوانید دیدگاهی ارسال کنید نیاز دارید که کاربر سایت شوید

ایجاد یک حساب کاربری

برای حساب کاربری جدید در سایت ما ثبت نام کنید. عضویت خیلی ساده است !

ثبت نام یک حساب کاربری جدید

ورود به حساب کاربری

دارای حساب کاربری هستید؟ از اینجا وارد شوید

ورود به حساب کاربری

  • کاربران آنلاین در این صفحه   0 کاربر

    هیچ کاربر عضوی،در حال مشاهده این صفحه نیست.

  • مطالب مشابه

    • توسط miladrahbari
      سلام،
      من در پروژه‌ی خودم از یک ویجت استفاده می‌کنم که داخل آن ویجت یک کلاس mainwindow وجود دارد. در داخل این کلاس من دو تا qquickwidget تعریف کردم تا از المان های درست شده در qml استفاده کنم.
      حالا سوالی که مطرح می‌شود این است. فکر کنید من در داخل یک ویجت ۱۰ بار از این component که در qml وجود دارد استفاده کردم. حالا مثلا مقدار یک component کیو ام ال رو تغییر می‌دهم و شکل ظاهری این المان تغییر می‌کند. وقتی که شکل این المان تغییر می‌کند کل صفحه (ویجت) repaint می‌شود؟؟؟ یا فقط اون المان خاص؟؟؟
      ممنون
    • توسط MohammadTahaRabie
      سلام 
      من میخواستم از یه QPlainTextedit خط خط نوشته هارو بخونم و هر کدوم رو به یک QThread ام بدم و از QPlainTextedit پاک بشه هر خطی که خونده شده رو 
      چطور میتونم اینکارو انجام بدم؟  
      C++ کم بلدم دارم یاد میگیرم ممنون میشم کمک کنید.
    • توسط GornerLabo
      با سلام خدمت اساتید محترم. اگر بخواهیم مقدار بازگشتی از رایت چندین دستور در مورد کلاس QSerialPort به صورت دلخواه ذخیره کنیم باید به چه شکل عمل کرد؟
       
      کد زیر برای دستوری است که عمل رایت دستور را انجام میدهد:
      port->setPortName(""); // COM PORT NAME port->setBaudRate(QSerialPort::Baud9600); port->setFlowControl(QSerialPort::NoFlowControl); port->setParity(QSerialPort::NoParity); port->setDataBits(QSerialPort::Data8); port->setStopBits(QSerialPort::OneStop); port->open(QSerialPort::ReadWrite); if(port->isOpen()) { port->write(); // command 1 for write port->write(); // command 2 for write port->write(); // command 3 for write port->write(); // command 4 for write } حالا برای هر دستور رایت یک مقدار بازگشتی دریافت خواهیم کرد. پس میتوان با connect سیگنال readyRead با یک اسلات به صورت کد زیر این مقدار بازگشتی رو ذخیره کرد:
      connect(port,SIGNAL(readyRead()),this,SLOT(MySlot())); QByteArray b = port->readAll(); QFile file("e:/test/raw.bin"); if(!file.open(QIODevice::WriteOnly | QIODevice::Append))return; file.write(b); file.flush(); file.close(); حالا اگر بخواهیم مقدار بازگشتی رو از دستور رایت مشخصی دریافت کنیم ، شیوه صحیح کار چطور است؟
    • توسط کامبیز اسدزاده
      کیوت (به انگلیسی: Qt) مجموعه‌ای از کتابخانه‌ها و سرآیندهای نوشته‌شده به زبان سی++ است که به برنامه‌نویس امکان توسعه آسان نرم‌افزارهای کاربردی را می‌دهد. کیوت شامل چندین کلاس برای کار با واسط گرافیکی، چندرسانه، ابزارهای پایگاه‌داده، شبکه و … است. نرم‌افزارهای نوشته شده با ابزار کیوت قادرند تا با استفاده از یک کامپایلر زبان سی‌پلاس‌پلاس برای طیف وسیعی از سیستم‌عامل‌ها از جمله گنو/لینوکس (نسخه‌های رومیزی و وسیله‌های قابل حمل)، ویندوز، ویندوز CE، مک‌اواس و … همگردانی شوند. بدین ترتیب حمل نرم‌افزار نوشته شده بدون تغییر در متن کد نوشته شده امکان‌پذیر است. از کیوت در زبان‌های برنامه‌نویسی متعددی مانند سی++ و جاوا و پایتون می‌توان استفاده‌کرد.
       
      جهت مشاهده‌ی مباحث مرتبط با این کتابخانه به این بخش مراجعه کنید.
×