Qt Creator - wątki

Qt Creator - wątki
Chev_Lucas
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 9 lat
  • Postów:50
0

Witam, potrzebuję użyć wątków w Qt Creator. Używam biblioteki QThread. Oto mój kod:

Kopiuj
class pojazd : public QThread
{
    Q_OBJECT;
public:
    void funkcja();
};

pojazd::funkcja()
{
    tu cos robi;
}

int main()
{
    pojazd nowy;
    nowy.start();
    nowy.funkcja();

}

Nowe wątki się tworzą, jednak funkcja jest wywoływana poza wątkiem, nawet gdy jest nazwana jako run(); Znalazłem gdzieś też kod, gdzie klasa była dziedziczona zwykle po QObject, a następnie jej sloty były łączone ze slotem started() nowego wątku. Jednak nie wiem jak użyć tej drugiej metody. Macie jakieś pomysły, dajcie znać proszę...

satirev
  • Rejestracja:prawie 14 lat
  • Ostatnio:około 4 lata
0
Chev_Lucas
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 9 lat
  • Postów:50
0

Dzięki, sprawdzałem już tę witrynę, jednak nie rozumiem tego fragmentu:

Kopiuj
QTcpSocket socket;
     // connect QTcpSocket's signals somewhere meaningful
     ...
     socket.connectToHost(hostName, portNumber);

Mam rozumieć że pisząc w main lub gdziekolwiek indziej:

Kopiuj
moja_klasa nowy;
nowy.start();

Uruchomię funkcję run w nowym wątku?

MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:minuta
5

Moja rada NIGDY nie dziedzicz po QThread! Tu ludzie popełniają strasznie dużo błędów. To powinno wyglądać tak:

Kopiuj
class TwojaKlasa : public QObject {
    Q_OBJECT
public:
     TwojaKlasa(QObject *parent = 0) QObject(parent) { ... }
     ~TwojaKlasa() { ... }

public slots:
     void twojSlot1() {
          ...
          emit czesciowyWynik("tada");
          ...
          emit zrobione();
     }
     void twojSlot2() {
          ...
     }
public signals:
     void czesciowyWynik(const QString& result);
     void zrobione();
}

// Gdzieś w kodzie by uruchomić coś w innym wątku:
QThread *thread = new QThread(jakisWlasciciel);
TwojaKlasa *worker = new TwojaKlasa(); // brak parenta jest tutaj obowiązkowy
worker ->moveToThread(thread);
thread->start(); // to tylko uruchamia wątek, który będzie czekał na pracę

connect(button1, SIGNAL(clicked()),
        worker, SLOT(twojSlot1()),
        Qt::QueuedConnection); // to połączanie będzie zlecało pracę wątkowi

connect(button2, SIGNAL(clicked()),
        worker, SLOT(twojSlot2())
        Qt::QueuedConnection); // to połączanie będzie zlecało pracę wątkowi

// opcjonalne czyszczenie wątku (poprawione w związku z tym tematem: http://4programmers.net/Forum/C_i_C++/224821-poprawne_zakonczenie_pracy_watku_qthread?p=988200#id988200):
//connect(worker, SIGNAL(destroyed(QObject*)),
//        thread, SLOT(quit()));
//connect(thread, SIGNAL(finished()),
 //       thread, SLOT(deleteLater()));

// do wykonania w jakim slocie
thread->wait();
thread->deleteLater()

Jak to działa? QThread ma event loopa, który czeka na coś do pracy (wątek pozostaje zawieszony - nic nie robi). Gdy leci sygnał do slotu worker-a, sygnał jest ładnie opakowywany i przesyłany do właściwego wątku (to właśnie robi moveToThread), wątek jest wybudzany i rozpoczyna przetwarzanie właściwego slotu. Jeśli więcej sygnałów zostanie wysłanych do obiektów w tym samym wątku, to są one kolejkowanie (idealne dla komunikacji sieciowej).
Cały pattern upraszcza proces komunikacji między wątkami.

Poza tym masz QThreadPool, który jest prosty w użyciu (nadaje się do zlecenia pojedynczej pracy), oraz freamework w przestrzeni nazw QtConcurrent do przetwarzanie kolekcji danych w wielu wątkach.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 5x, ostatnio: MarekR22
Zobacz pozostały 1 komentarz
satirev
Mógłbyś skrobnąć coś więcej na temat, tego dlaczego nie dziedziczyć po QThread?
MarekR22
tu http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/ autor klasy bije się w pierś, że nawet w dokumentacji jest źle.
Endrju
Bo jak się robi to źle to wychodzą głupoty. To jest bezpieczniejsze bo zawsze działa - w Qt 5 to jest już rekomendowany sposób. Natomiast jeżeli potrzebujesz większej kontroli nad wątkami i wiesz co robisz to nic nie stoi na przeszkodzie, żeby dziedziczyć. Poczytaj tutaj: http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/ (Głównie chodzi o sygnały/sloty oraz filozofię tego, co powinna zawierać klasa zarządzająca wątkiem)
satirev
Dzięki, poczytam.
Chev_Lucas
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 9 lat
  • Postów:50
0

Mam parę pytań, bo coś źle robię.

  • czy mogę dać sobie wskaźnik QThread *thread jako składnik klasy i przydzielić mu pamięć w konstruktorze klasy?
  • w ciele slotu, który chcę wywołać w nowym wątku, muszę umieścić exec, lub emit?
  • czy można przyłączyć slot do jakieś zwykłej metody tej klasy, zamiast do zdarzenia clicked przycisku (chcę tworzyć wątki i wywoływać w nich funkcje w jakieś innej funkcji)

Dodam jeszcze, że zrobiłem prosty program, gdzie w slocie, który chcę odpalić w nowym wątku przesuwam w pętli label o 1 w dół. Po kliknięciu przycisku, po którym się powinna wykonać akcja tego slotu wyskakuje błąd:
Microsoft Visual C++ Runtime Library
This application has requested the Runtime to terminate it in an unusual way.

Kompilator wyrzuca:
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 55a278. Receiver 'label' (of type 'QLabel') was created in thread 3974d0", file kernel/qcoreapplication.cpp, line 532
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
QObject::killTimers: timers cannot be stopped from another thread

edytowany 2x, ostatnio: Chev_Lucas
MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:minuta
1
Chev_Lucas napisał(a):
  • czy mogę dać sobie wskaźnik QThread *thread jako składnik klasy i przydzielić mu pamięć w konstruktorze klasy?
    Możesz ale ja by tak nie robił.
Chev_Lucas napisał(a):
  • w ciele slotu, który chcę wywołać w nowym wątku, muszę umieścić exec, lub emit?
    ciało sloty ma wyglądać tak jak ty chesz. Na pewno nie ma być tam exec, a emit dałem jako przykład jak należy przekazać wynik działania slotu.
Chev_Lucas napisał(a):
  • czy można przyłączyć slot do jakieś zwykłej metody tej klasy, zamiast do zdarzenia clicked przycisku (chcę tworzyć wątki i wywoływać w nich funkcje w jakieś innej funkcji)
    źródłem sygnału może być cokolwiek, znowu dałem buttona i slota clicked jako przykład.
Chev_Lucas napisał(a):

Dodam jeszcze, że zrobiłem prosty program, gdzie w slocie, który chcę odpalić w nowym wątku przesuwam w pętli label o 1 w dół. Po kliknięciu przycisku, po którym się powinna wykonać akcja tego slotu wyskakuje błąd:
Microsoft Visual C++ Runtime Library
This application has requested the Runtime to terminate it in an unusual way.
Nie wolno modyfikować elementów UI z innych wątków. Jeśli zrobisz to przez sygnały i sloty to Qt ładnie prześle sygnał do właściwego wątku w którym slot ma być wykonany.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
Chev_Lucas
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 9 lat
  • Postów:50
0

Utworzyłem nową klasę, której nie przydzielam do żadnego wątku. Klasa ta posiada dwa sloty, w których steruję labelami z formy. W klasie, którą ładuję do wątku mam dwa sygnały z identycznymi listami argumentów jakie mają sloty z pierwszej klasy.
Jednak, gdy wywołuję funkcję pisząc

Kopiuj
nowy.fun1(label, x, y);

to otrzymam żądany efekt, jak np. przesunięcie się etykiety.
Ale pisząc w klasie umieszczonej w wątku

Kopiuj
emit signal1(label, x, y);
//oraz gdzieś po utworzeniu klasy:
connect(this, SIGNAL(signal1(QLabel*, int, int)), &nowy, SLOT(fun1(QLabel*, int, int)), Qt::QueuedConnection);

Nie widać żadnych efektów. Nie wiem dlaczego :/

edytowany 2x, ostatnio: Chev_Lucas
Chev_Lucas
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 9 lat
  • Postów:50
0

Już sobie poradziłem, emitowany sygnał jest odbierany prawidłowo i wykonuje się akcja z przypisanego slotu.
Natomiast, mam inny problem, potrzebuję w trakcie działania powiedzmy 8 wątków czytać jedną zmienną składową klasy korzystając z tablicy wskaźników do obiektów tej klasy.
Gdy wątki jeszcze nie są odpalone to działa ładnie, ale gdy wątki działają program się sypie. Pewnie dlatego, że jednocześnie zmienna jest zapisywana przez jeden obiekt w jednym wątku i odczytywana przez inny.

tomepaw
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Lublin
  • Postów:218
0

Zainteresuj się mutexami (http://doc.qt.digia.com/qt/qmutex.html), właśnie po to one są :)

Chev_Lucas
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 9 lat
  • Postów:50
0

A wiecie może jak podczas działania wątku wywołać funkcję tak, żeby czekać na jej zakończenie, a ta funkcja się wykonywała w wątku głównym (chodzi mi o dokonywanie losowania - jak mam je w wątku pobocznym, to coś nie działa ;/)
Czy da się zrobić coś takiego, że funkcja tylko w pewnym miejscu jest w wątku, potem wykona coś poza wątkiem i tak w pętli...

edytowany 1x, ostatnio: Chev_Lucas
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)