• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[C++/Qt] Wątki, jeden zatrzymuje drugi?

VPS Starter Arubacloud
0 głosów
524 wizyt
pytanie zadane 21 czerwca 2017 w C i C++ przez wanttobeanengineer Obywatel (1,120 p.)

Witam wszystkich,

Napotkałem problem, przy próbie napisania aplikacji na wątkach.

Aplikacja ta ma rysować linię przy pomocy biblioteki QGraphicsItem. 

Konkretnie chodzi o to, że mam narysowaną czarną linie, po naciśnięciu przycisku po tej czarnej linii ma się przesuwać czerwony prostokąt (to już mam gotowe).

Docelowo chcę zrobić, aby można było wypuścić po naciśnięciu innego przycisku (lub drugi raz tego sameg) kolejny prostokąt, tak aby w przypadku gdy pierwszy jeszcze jest "na trasie" obydwa poruszały się jednocześnie.

Uruchamiam jeden wątek z jednego przycisku - leci prostokąt
Jednak gdy nacisnę drugi przycisk i wypuszczę drugi prostokąt, ten pierwszy się zatrzymuje, dopiero w momencie gdy drugi doleci do końca, ten pierwszy znowu rusza.

Czy istnieje jakiś sposób, aby te dwa wątki pracowały współbieżnie?
Chyba, że robie coś źle i wychodzi na to że to jest jeden wątek.

Proszę o jakieś wskazówki i wyrozumiałość.

Kod wygląda mniej więcej tak

Klasa wątku:

#include <QThread>
class myThread : public QThread
{
    Q_OBJECT
public:
    explicit myThread(QObject *parent = 0);
    void run();
signals:
    void go(int);

};


Klasa mainwindow:

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    myThread *mThread, *mThread2;
private:
    Ui::MainWindow *ui;
  //  QGraphics ....

public slots:
    void on_go(int);
    void on_go2(int);

private slots:
    void on_btnAdd1_clicked();
    void on_btnAdd2_clicked();
};


Definicja metody run():
 

void myThread::run()
{
    emit go(1); 
   // 1 w parametrze dla tego, że wyswietlalo mi warningi wiec dodalem do metod parametry
}

 

Definicja on_go() (analogiczna jest on_go2):
 

void MainWindow::on_go(int watek)
{
       Draw(); //funkcja rysująca czerwony prostokąt
}

Zdarzenie po wcisnieciu przycisku (analogicznie wyglada drugi przycisk):

void MainWindow::on_btnAdd1_clicked()
{
   mThread -> start();
}

I connecty (oczywiście w konstruktore mainwindow:
 


    mThread = new myThread(this);
    connect(mThread,SIGNAL(go(int)),this,SLOT(on_go(int)));

    mThread2 = new myThread(this);
    connect(mThread2,SIGNAL(go(int)),this,SLOT(on_go2(int)));


Wydaje mi się, że problemem jest to, że emit na go() wywołuje tylko raz, a nie w pętli (bo w pętli rysuje te prostokąty, które się poruszają po czarnejlinii). Jednak w run() nie mogę dać tej pętli, ponieważ pętla leci do pewnej wartości, która jest pobierana ze spinboxa, a w run() nie mam dostępu do obiektów ui.

komentarz 21 czerwca 2017 przez jankustosz1 Nałogowiec (35,880 p.)
Nie znam qt, ale nie potrzebujesz mieć do tego 2 wątków, wystarczy 1.
komentarz 21 czerwca 2017 przez wanttobeanengineer Obywatel (1,120 p.)
W takim razie jak to zrobić na jednym? W Qt jestem zielony prawie tak jak w wątkach.

Nie wiem czy dobrze wytłumaczyłem problem, ma to ilustrować powiedzmy dwa samochody na wąskiej uliczce, które jadą jeden za drugim. Oczywiście prędkości jazdy tych samochodów mogą się różnić, i ten drugi nie może wyprzedzić pierwszego.

Dodatkowo musi być możliwość wyruszenia tymi samochodami w dowolnym momencie, czyli:
Naciskam btn1, rusza samochód pierwszy z prędkością określoną w spinboxie.
Naciskam btn2 (lub lepiej nawet drugi raz btn1) i wyrusza kolejny samochód, który pobiera predkość z tego samego spinboxa.

Jak inaczej to mogę zrobić?
komentarz 21 czerwca 2017 przez jankustosz1 Nałogowiec (35,880 p.)
Może pisałeś kiedyś coś w SFML' u? Tam się pisze wszystko w jednym wątku. W qt chyba nie masz żadnej głównej pętli która mogłaby cały czas działać, więc rzeczywiście musisz zrobić 1 dodatkowy wątek, które będzie się odpalał jeżeli jeden z samochodów zostanie odpalony. Potrzebujesz dla każdego samochodu timera i boola czy jest odpalony. Za każdym obiegiem pętli będziesz mnożył to co wyliczył timer z jakąś prędkością samochodu i zerował timer.

2 odpowiedzi

0 głosów
odpowiedź 21 czerwca 2017 przez j23 Mędrzec (194,920 p.)

Nie znam Qt, ale nie zdziwiłbym się, gdyby metody on_go i on_go2 wykonywały się w wątku głównym (GUI). Ergo: to, że odpalasz je w dwóch oddzielnych wątkach nie zmienia faktu, że wykonywane są sekwencyjnie (zatem sens użycia wątków żaden).

komentarz 21 czerwca 2017 przez wanttobeanengineer Obywatel (1,120 p.)
Właśnie też tak myślałem. Pytanie jak zrobić, aby wykonywały się równoległe na dwóch wątkach.

Nie jestem pewien czy potrzebne są te dwie metody, bo de facto robią dokładnie to samo i na tym samym obiekcie z formatki.

Utworzyłem dwie funkcje, bo w poprzednim projekcie robiłem dwa liczniki które liczyły równolegle, ale wartości były wyświetlane w dwóch labelach. Z tego powodu jedna metoda wyświetlała w jednym, druga w drugim. Pewnie dało by się zrobić to w jednej metodzie, ale to do tego dochodził będę później (małymi kroczkami a do celu).
komentarz 21 czerwca 2017 przez j23 Mędrzec (194,920 p.)
edycja 21 czerwca 2017 przez j23

Pytanie jak zrobić, aby wykonywały się równoległe na dwóch wątkach.

Wywołać wspomniane metody bezpośredni w wątku, bez pośrednictwa sygnałów/slotów. Pytanie tylko, czy biblioteka GUI jest thread-safe. Generalnie rysowanie po oknie powinno odbywać się w tym samym wątku, w którym pracuje okno. W przypadku jakichś animacji używa się timerów okienkowych i rysuje "na raty", albo klatka po klatce. W wątkach co najwyżej możesz przygotować "ciężkie" bitmapy, które później wrzucisz na okno.

 

--- dodane ---

Z tego, co widzę w dokumentacji, przy tworzeniu połączeń metodą connect możesz wybrać typ połączenia. Domyślnie jest ustawiany AutoConnection, co oznacza, że przy komunikacji międzywątkowej zdarzenie jest kolejkowane, czyli tak jak pisałem. Ustawiając DirectConnection, zdarzenie powinno wykonać się w kontekście wątka wywołującego.


 
komentarz 21 czerwca 2017 przez wanttobeanengineer Obywatel (1,120 p.)
edycja 21 czerwca 2017 przez wanttobeanengineer
Przy DirectConnection wyłącza mi się aplikacja po naciśnięciu na przycisk, który powinien wypuścić drugi prostokąt.
komentarz 22 czerwca 2017 przez j23 Mędrzec (194,920 p.)
Zapewne brak synchronizacji powoduje błąd.
0 głosów
odpowiedź 21 czerwca 2017 przez wanttobeanengineer Obywatel (1,120 p.)
edycja 22 czerwca 2017 przez wanttobeanengineer
Chyba wiem już co robiłem źle.

Mam jednak kolejne pytanie.

Używając klasy, która dziedziczy z QThread, chciałbym aby metoda run(), która jest składnikiem klasy dziedziczącej z QThread, miała dostęp do obiektów z formularza (ui).

Jak mogę tego dokonać?
komentarz 22 czerwca 2017 przez wanttobeanengineer Obywatel (1,120 p.)
Przekazałem parametry do konstruktora klasy z moim wątkiem, a w tej klasie dodałem również atrybuty takie jak chciałem mieć w metodzie run(), następnie w metodzie run() wykorzystałem sobie te atrybuty do tego co było mi potrzebne.

Program kompuluje się ładnie, ale po próbie uruchomienia w komunikatach aplikacji wyświetla: "Zakończono z kodem 255". O czym zapomniałem?
komentarz 22 czerwca 2017 przez j23 Mędrzec (194,920 p.)

Prawdopodobnie o tym, o czym pisałem w poprzednim komentarzu.

 

Tu masz fragment z dokumentacji:

Many Qt classes are reentrant, but they are not made thread-safe, because making them thread-safe would incur the extra overhead of repeatedly locking and unlocking a QMutex.

Użycie wątków wbrew pozorom aż takie proste nie jest. Prawie zawsze wiąże się z problemem synchronizacji (której u Ciebie - jak domniemam - nie ma).

Podobne pytania

0 głosów
2 odpowiedzi 235 wizyt
pytanie zadane 30 kwietnia 2018 w C i C++ przez neemo Nowicjusz (150 p.)
+1 głos
4 odpowiedzi 401 wizyt
pytanie zadane 21 czerwca 2017 w C i C++ przez wanttobeanengineer Obywatel (1,120 p.)
–2 głosów
2 odpowiedzi 500 wizyt
pytanie zadane 27 marca 2020 w C i C++ przez Eriss69 Gaduła (4,470 p.)

92,454 zapytań

141,262 odpowiedzi

319,089 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...