Asynchroniczne wątki

0

Mam problem z uzyskaniem asynchroniczności wątków. Kod wygląda tak:
server.h:

class Server{

public:
    void send(int time){
        std::async(std::launch::async, Server::work, time);

    }

    static void work(int time){
        std::this_thread::sleep_for(std::chrono::seconds(time));
        std::cout<<"Wątek "<<time<<" zakonczony\n";
    }
};

main.cpp:

#include "server.h"

int main()
{
    Server().send(3);
    Server().send(2);
}

Na wyjściu dostaję:

Wątek 3 zakonczony
Wątek 2 zakonczony

A chciałbym otrzymać:

Wątek 2 zakonczony
Wątek 3 zakonczony

Gdzie robię błąd?

3

Przy wyjściu ze scope destruktor std::future, zwracanego przez std::async, blokuje wątek aż do wykonania funkcji przekazanej do std::async. Możesz użyć std::thread().detach(), albo trzymać zwracane future w jakimś kontenerze:

class Server {
public:
    std::vector<std::future<void>> futures;
    
    void send(int time) {
        auto f = std::async(std::launch::async, Server::work, time);
        futures.push_back(std::move(f));
    }
 
    static void work(int time) {
        std::this_thread::sleep_for(std::chrono::seconds(time));
        std::cout<<"Wątek "<<time<<" zakonczony\n";
    }
};
 
int main()
{
    Server server{};
    server.send(3);
    server.send(2);
}

Tylko pamiętaj, żeby usuwać te, które się już zakończyły.

1

Nie trzeba odłączać wątków. wykonanie get() na futures wystarcza.
Nie masz gwarancji kolejności wykonania zleceń asynchronicznych bo nie masz kontroli nad planistą. Raczej zrób to w trybie RAII. Tu masz przykład:

#include <future>
#include <iostream>
#include <vector>

class Server{
    std::vector<std::future<void>> ftrs; 
public:
    void send(int time) {
        auto ftr = std::async(std::launch::async, &Server::work, this, time);
        ftrs.push_back(std::move(ftr));
    }
 
    void work(int time) {
        std::this_thread::sleep_for(std::chrono::seconds(time));
        std::cout << "Wątek id = " << std::this_thread::get_id() << " z czasem = "
            << time << "s zakonczony\n";
    }
    ~Server() {
        std::cout << "Destructor ftrs size = " << ftrs.size() << '\n';
        for(auto& ftr: ftrs) {
            ftr.get();
        }
    }
};

int main()
{
    Server s;
    s.send(3);
    s.send(2);
}
0

Zauważcie, że ja wywołuję funkcję send na dwóch różnych obiektach.
Jeśli wywołam ją na tym samym obiekcie, tak jak w waszych kodach, to działa poprawnie (nawet bez tworzenia vectora).

1

Jeśli wywołam ją na tym samym obiekcie, tak jak w waszych kodach, to działa poprawnie (nawet bez tworzenia vectora).

Mam wrażenie, że jednak nie: https://wandbox.org/permlink/rIHGR1v92MvrdifD.
Nie wiem dlaczego chcesz tworzyć za każdym razem nowy obiekt, ale możesz zwyczajnie zwracać std::future z send() i trzymać je gdzieś indziej.

1

Moim zdaniem niewygodne.. ale jak się upierasz... :

#include <future>
#include <chrono>
#include <vector>
#include <iostream>

class Server{
    static std::vector<std::future<void>> ftrs; 
public:
    static void send(int time){
        auto ftr = std::async(std::launch::async, Server::work, time);
        ftrs.push_back(std::move(ftr));
    }
 
    static void work(int time){
        std::this_thread::sleep_for(std::chrono::seconds(time));
        std::cout<<"Wątek id = " << std::this_thread::get_id()
            << " z time = " << time <<"s zakonczony\n";
    }
    static void clean() {
        for(auto& ftr: ftrs) {
            ftr.get();
        }
    }
};

std::vector<std::future<void>> Server::ftrs;
 
int main()
{
    Server::send(3);
    Server::send(2);
    Server::clean();
}
0
bl4ster napisał(a):

Zauważcie, że ja wywołuję funkcję send na dwóch różnych obiektach.

Nie czaję, jakie to mam mieć znaczenie???

Jeśli wywołam ją na tym samym obiekcie, tak jak w waszych kodach, to działa poprawnie (nawet bez tworzenia vectora).

U mnie bez klas działa jak z klasami. Możesz podać dokładny kod, kiedy to działa jak sobie życzysz?

Wyrzuciłem klasy w ogóle, zrobiłem tak (dodałem get_id):

#include <future>
#include <iostream>

void work(int time) {
    std::this_thread::sleep_for(std::chrono::seconds(time));
    std::cout<<"Wątek " << std::this_thread::get_id() << " -- " <<time<<" zakonczony\n";
}

void send(int time) {
    std::async(std::launch::async, work, time);
}

int main() {
    send(5);
    send(2);
}

i dostałem:

Wątek 140679349307136 -- 5 zakonczony
Wątek 140679349307136 -- 2 zakonczony

-- ten sam id wątku.

Twoja wersja z dodanym id daje to samo (z dokładnością do numeru wątku):

Wątek 140563016447744 -- 3 zakonczony
Wątek 140563016447744 -- 2 zakonczony

A w ogóle std::async nie musi chyba tworzyć nowego wątku, więc this_thread odwołuje się (i każe spać) wątkowi głównemu...

1 użytkowników online, w tym zalogowanych: 0, gości: 1