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

Wątki i obiekty, czy wątek może stworzyć obiekt w konstruktorze? C++

Object Storage Arubacloud
0 głosów
891 wizyt
pytanie zadane 3 grudnia 2017 w C i C++ przez Łukasz Wasilewski Mądrala (5,190 p.)

Cześć, 

dostałem na studiach zadanie by z pomocą wątków stworzyć trójkąt sierpińskiego w tablicy[x][x] i zapisać go do pliku.

Stworzyłem więc klasę arrayHandler oraz triangle. Na początku w main tworzę instancję klasy tablicy oraz trójkąta z początkowymi punktami. Trójkąt zawiera wskaźnik na obiekt arrayHandler. W konstruktorze trójkąta uruchamiam funkcję divide() w której tworzę tablice nowych wierzchołków array[4][3][2] (4 nowe trójkąty po 3 wierzchołki każdy ma x i y ). 
 

Teraz wydawało mi się że skończyłem program, gdyż wystarczy że w funkcji divide() stworzę 4 nowe wątki dla każdego nowego trójkąta, jednak to tu jest największy problem - jak?

//Próbowałem tak:
thread th(new triangle(*aH, p1, p2, p3));

(aH to arrayHandler natomiast p1, p2, p3 to wierzchołki)

pokazuje błąd:

'std::invoke': no matching overloaded function found   

natomiast:

triangle fTrian(*myArr, p1, p2, p3);
thread first(fTrian.divide);

pokazuje:

'triangle::divide': non-standard syntax; use '&' to create a pointer to member

Mimo, że funkcja tam jest.

 

Chciałbym to zrozumieć, ktoś posłuży radą?
 

2 odpowiedzi

+3 głosów
odpowiedź 3 grudnia 2017 przez adrian17 Ekspert (344,860 p.)
wybrane 3 grudnia 2017 przez Łukasz Wasilewski
 
Najlepsza

thread th(new triangle(*aH, p1, p2, p3));

`new Triangle` alokuje i konstruuje Triangle, po czym zwraca wskaźnik. Wątek potrzebuje coś, co da się wywołać - wskaźnik na Triangle nim nie jest.

thread first(fTrian.divide);

Metody też nie możesz tak po prostu przekazać, bo sama metoda by nie wiedziała, na jakim obiekcie działać.

Najprostszym rozwiązaniem będzie pewnie lambda w tym stylu:

std::thread my_thread([=](){
    Triangle triangle(*myArr, p1, p2, p3);
    triangle.divide();
});

Albo z osobną funkcją:

void thread_main(a, b, c, d) { // jakies tam typy argumentow
    Triangle triangle(a, b, c, d);
    triangle.divide();
}

// ...
    std::thread my_thread(thread_main, *myArr, p1, p2, p3);

 

komentarz 3 grudnia 2017 przez Łukasz Wasilewski Mądrala (5,190 p.)
Teraz to rozumiem i nauczyłem się przy okazji czegoś nowego (lambda). Bardzo dziękuję za szybką odpowiedź.
komentarz 19 grudnia 2017 przez Łukasz Wasilewski Mądrala (5,190 p.)
Mam pytanie odnośnie pierwszej Lambdy, czy da się odwołać do stworzonego w niej obiektu już poza nią?
komentarz 19 grudnia 2017 przez mokrowski Mędrzec (155,460 p.)
No tak... ciężka kopia obiektu przez lambdę. Przejedziesz się na tym w trakcie sprawdzania wydajności.
komentarz 19 grudnia 2017 przez adrian17 Ekspert (344,860 p.)

Um... to był przykład.

Poza tym:

- przekazanie przez kopię może być szybsze, jeśli kopiowane obiekty są lekkie

- nawet jeśli kopiowane obiekty są złożone (np kilka struktur kilku floatów) to to wciąż prawdopodobnie będzie o rzędy wielkości tańsze od rozkręcenia std::thread.

- przekazanie przez kopię w takim prostym przypadku jest bezpieczniejsze, bo nie musisz się przejmować ewentualnymi modyfikacjami między wątkami

	int x = 10;
	std::thread my_thread([&]{
	    x; // 10 lub 11
	});
	x = 11;
int x = 10;
std::thread my_thread([=]{
    x; // 10
});
x = 11;

 

komentarz 19 grudnia 2017 przez mokrowski Mędrzec (155,460 p.)

Dylemat kopia/brak_kopii raczej lepiej rozwiązać na gruncie mutable/immutable. Tym bardziej że mówisz o "zmienialności lub jej braku". Uruchomienia wątku nie unikniesz w tym przykładzie a kopii możesz :-) Załatwienie braku zmiany można przecież osiągnąć przez stały wskaźnik lub wskaźnik na stałą (jeśli to usprawiedliwione) std::cref(). Zresztą jak wiadomo warto zmierzyć na rzeczywistym przykładzie. Co do zasady jednak, lepiej nie kopiować jeśli to nie jest absolutnie konieczne. Stos wątku nie jest duży w większości implementacji a od "prywatności danych wątku" jest thread_local_storage.

IMHO. Bo nie widać całości kodu. Tam mogą być niespodzianki :-)

komentarz 19 grudnia 2017 przez mokrowski Mędrzec (155,460 p.)
Podtrzymuję:

 W 1 przykład mówi o lambdzie dla int a w przykładzie masz więcej danych oraz ew. niepotrzebne tworzenie obiektu.

W 2 (CP. 31) zastrzeżenie "Note: Defining “small amount” precisely is impossible" ergo zmierzyć jak napisałem wyżej. W przykładzie lecą 3 x Point'y i kopia tablicy!

W 3, F.16 potwierdza co napisałem.

Obydwaj nie wiemy "jak ciężki ma Triangle i Point's" i z jakiego środowiska uruchomienia korzysta (vide jak ciężkie jest kreowanie wątku). Nie spieramy się o przesłanie int'a czy innego POD'a tylko struktur których nie znamy  i (spekulując) nie wyglądają na "lekkie jak int" :-) A w przykładach Core Guide masz POD'y jako poprawne. Chyba że podejrzewasz mnie o propagowanie tezy że int'y także bym przesyłał jako referencje :-) Jeszcze się dobrze czuję :-)

Jeszcze raz... po co kopiować jak obiekt już istnieje i nie widać w algorytmie pytającego operacji usprawiedliwiających kopie? Inną jeszcze sprawą jest dehermetyzacja lambdy (bo to robi [=]). Why? Na grzyba tu lambda (na marginesie)?
+1 głos
odpowiedź 19 grudnia 2017 przez mokrowski Mędrzec (155,460 p.)

Konwencje wywołań:

#include <thread>
#include <iostream>

void work_function() {
    std::cout << "I'm work_function().\n";
}

class WorkClassFunctor {
public:
    void operator()() {
        std::cout << "I'm WorkClassFunctor object.\n";
    }
};

class Worker {
public:
    void work() {
        std::cout << "I'm Worker work() function.\n";
    }
};

int main() {
    WorkClassFunctor wc;
    Worker worker;

    std::thread thr1(work_function);
    std::thread thr2(wc);
    std::thread thr3(&Worker::work, &worker);
    std::thread thr4([](){ std::cout << "I'm lambda.\n"; });

    for(auto& th: { &thr1, &thr2, &thr3, &thr4 }) {
        th->join();
    }
}

Oczywiście na konsoli możesz uzyskać "przemieszane" wyjście ale to z powodu braku synchronizacji której dla jasności tu nie wprowadzałem.

Jeszcze ważna rzecz... Przekazanie argumentu. Zawsze jest przez kopię. Stąd jeśli chcesz mieć referencję, opakowujesz w std::ref():

#include <thread>
#include <iostream>

void func_copy_args(int a) {
    std::cout << "func_copy_args() a = " << a << std::endl;
}

void func_ref_args(int& a) {
    std::cout << "func_ref_args() before a = " << a << std::endl;
    a += 100;
    std::cout << "func_ref_args() after a = " << a << std::endl;

}
int main() {
    int a = 102;
    std::cout << "in main() before running threads... a = " << a << std::endl;

    std::thread thr1(func_copy_args, a);
    std::thread thr2(func_ref_args, std::ref(a));

    for(auto& th: { &thr1, &thr2 }) {
        th->join();
    }
    std::cout << "in main() after running threads... a = " << a << std::endl;
}

 

komentarz 19 grudnia 2017 przez Łukasz Wasilewski Mądrala (5,190 p.)

Przepraszam, że odpisuję dopiero teraz, ale wolałem wpierw skończyć zadanie smiley.

Bardzo dziękuję za pomoc, bardzo pomógł mi ten wpis w zrozumieniu wątków i ukończenia zadania, a jako bonus zobaczyłem nowy sposób na zastosowanie pętli for. ^^. 

Podobne pytania

0 głosów
1 odpowiedź 542 wizyt
pytanie zadane 2 października 2017 w C i C++ przez Programeł Gaduła (3,500 p.)
0 głosów
1 odpowiedź 452 wizyt
pytanie zadane 16 kwietnia 2018 w C i C++ przez niezalogowany
0 głosów
1 odpowiedź 917 wizyt

92,551 zapytań

141,393 odpowiedzi

319,523 komentarzy

61,936 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

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy 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!

...