samedi 22 juillet 2023

How to check the close-to-verbose status of a thread's "shared state" or "memory state"? How to not make calls to std::async( ) block?

My method that uses multithreading is:

typedef std::pair<std::vector<char>, int> PATH_PAIR;

std::list<PATH_PAIR>
TSP::return_all_paths_list(
    PATH_PAIR                                       parent_path_pair,
    std::list<char>                                 unvisited_cities
) {
    std::list<PATH_PAIR>                            subseq_paths_list;
    std::vector<std::future<std::list<PATH_PAIR>>>  future_vec;
   
    if (unvisited_cities.empty()) { // END RECURSION
        // NON-REALTED CODE
        return subseq_paths_list;
    }

    for (const char curr_city: unvisited_cities) {   // DO RECURSION
        PATH_PAIR                           new_path_pair_addr = parent_path_pair;
        std::list<char>                     subseq_unvisited_cities = unvisited_cities;
        std::future<std::list<PATH_PAIR>>   returned_path_list_future;
        // NON-RELATED CODE
        returned_path_list_future = std::move(std::async(std::launch::async, return_all_paths_list, this, new_path_pair_addr, subseq_unvisited_cities));

        future_vec.push_back(returned_path_list_future);
    }

    while (!future_vec.empty()) {   // GET-VALUES-WHILE
        for (std::future<std::list<PATH_PAIR>>& curr_future : future_vec)  // GET-VALUES-FOR
            if (curr_future.valid()) {
                subseq_paths_list.merge(curr_future.get());
            }
        // future_list.remove_if(
        //     [] (std::future<std::list<PATH_PAIR>> input_future) -> bool {
        //         return !input_future.valid();
        //     }
        // );
    }

    return subseq_paths_list;
}

I have a few questions:

  1. As per cppreference std::async:

    If the std::future obtained from std::async is not moved from or bound to a reference, the destructor of the std::future will block at the end of the full expression until the asynchronous operation completes, essentially making code such as the following synchronous:

    std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f()
    std::async(std::launch::async, []{ g(); }); // does not start until f() completes
    

    a. What do the words "move from" and "bound to a reference" mean?
    b. Will wrapping the call to std::async inside an std::move make the calls to std::async not block in the suceeding iterations of // DO RECURSION loop?
    c. Or are the calls in the above two lines blocking just because lambdas are used?

  2. How do I check if a shared state is ready?
    a. A call to std::future::wait (and std::future::get) blocks until the shared state is ready. The thing is, I don't want to wait. I want to check if a shared state is ready, if it's not, I want to move on to the next std::future object. How do I do that? std::future::valid only checks if a shared state is associated with an object.
    b. Also, again as per cppreference std::async, the return value of a call to std::async is:

    std::future referring to the shared state created by this call to std::async

    So, in the //GET VALUES loops, the std::future obtained will always return true if std::future::valid is called on it (provided std::future::get isn't called on it yet). If I remove an std::future after an std::future::get is called, I don't have to check the validity of the std::future objects. But, none of the remove element STL methods I've tried don't work, there's compile time errors spanning to about twice the height of my display.

    I thought, maybe calling std::future::get also destroys the object in addition to releasing the shared state, but a small test piece of code I wrote:

    int mul2(int x) {return x << 1; }
    int main() {
        std::vector<std::future<int>> fut_vec;
        for (int i = 0; i < 5; i++) {
            fut_vec.push_back(std::move(std::async(std::launch::async, mul2, i)));
        }
        for (std::vector<std::future<int>>::iterator itr = fut_vec.begin(); itr != fut_vec.end(); itr++) {
            if (itr->valid()) {
                std::cout << "Value: " << itr->get() << " | No. of vec elements(futures): " << fut_vec.size() << std::endl;
            }
        }
    
        return 0;
    }
    

    reveals the fut_vec.size() always returns the same integer.

    How do I destroy an std::future object?

I recently got into multithreading, so now I'm trying to implement the travelling salesman problem using multithreading.

Aucun commentaire:

Enregistrer un commentaire