简体   繁体   中英

C++ multithreading: detecting a returned value from the quickest of multiple threads

Details:

In the program below, multiple threads (in this case only 2 for simplicity) listen out for the same value of 66 to be returned from the functions, following some logic in both functions that produces the result 66 .

The threads use async , and the values of 66 are returned using futures . A while loop is used in an attempt to continually check the status of threads one and two to check if either of them have completed, in which case the fastest result from either of the threads is then fetched and used in some calculation.

Goal:

  1. Out of the two threads , to detect which one of them is first to return the value of 66
  2. As soon as a thread returns 66 (regardless of if the other thread has completed), the returned value is then made available in main() for some further simple arithmetic to be performed upon it
  3. If a thread returns 66 and arithmetic is performed upon this value in main() , and then the other thread later on delivers 66 as well, this second returned value should not be used in any calculations

Please note: before deciding to post this question, the following resources have been consulted:

  1. How to check if thread has finished work in C++11 and above?
  2. Using Multithreading two threads return same value with different inputs?
  3. C++ threads for background loading
  4. Future returned by a function
  5. Start multiple threads and wait only for one to finish to obtain results

Problems and Current Ouput:

Currently, the program always outputs that the first thread to finish is rf1 , even if the code in function1 is substantially slower (eg a for loop with 1000 iterations in function1 , and a for loop with 10 iterations in function1 ). This leads me to believe there is some sort of blocking behaviour somewhere that I may have introduced?

Program Attempt:

    #include <future>
    #include <iostream>
    #include "unistd.h"

    double function1() {
    
        // Logic to retrieve _value
        // ...
        double _value = 66;
    
        return _value;
    }
    
    double function2() {
        // Logic to retrieve _value
        // ...
        double _value = 66;
    
        return _value;
    }
    
    int main() {
    
        double ret_value = 0;
    
        auto rf1 = std::async(std::launch::async, function1);
        auto status1 = rf1.wait_for(std::chrono::nanoseconds(1));
    
        auto rf2 = std::async(std::launch::async, function2);
        auto status2 = rf2.wait_for(std::chrono::nanoseconds(1));
    
        while (true) {
    
            if (status1 == std::future_status::ready) {
                std::cout << "RF1 FINISHED FIRST" << std::endl;
    
                // No longer need returned val from 2nd thread.
                // Get result from 1st thread
                ret_value = rf1.get();
                break;
            }
            else if (status2 == std::future_status::ready) {
                std::cout << "RF2 FINISHED FIRST" << std::endl;
    
                // No longer need returned val from 1st thread.
                // Get result from 2nd thread
                ret_value = rf2.get();
                break;
            }
    
            else if (status1 != std::future_status::ready) {
    
                // RF1 not finished yet
                status1 = rf1.wait_for(std::chrono::nanoseconds(1));
            }
            else if (status2 != std::future_status::ready) {
    
                // RF2 not finished yet
                status2 = rf2.wait_for(std::chrono::nanoseconds(1));
            }
        }
    
        // Do some calculations on the quickest
        // returned value
        double some_value = ret_value + 40;
    
        return 0;
    }

Questions:

Q1. Can the program be modified in any way to detect the fastest thread to return so that the returned value of 66 can be used within main() for further calculations?

Q2. Has the while loop introduced any sort of blocking behaviour?

If anyone may be able to advise or point to some resources that could aid in solving this dilemma, it would be greatly appreciated. So far, it has been a challenge to find multithreading documentation that exactly matches this scenario.


EDIT:

Based on a helpful answer from @jxh, the else if conditions instructing the WHILE loop to continue waiting have been removed, as seen further below.

Furthermore, some logic has been added to function1 and function2 to see which one will finish first. As seen in the code, function1 has 98 iterations and function2 has 100 iterations, yet the output continually says that function2 has finished first:

#include <future>
#include <iostream>
#include "unistd.h"

double function1() {

    // Logic to retrieve _value
    for (int i = 0; i < 98; i++) {
        std::cout << std::endl;
    }
    double _value = 66;

    return _value;
}

double function2() {

    // Logic to retrieve _value
    for (int i = 0; i < 100; i++) {
        std::cout << std::endl;
    }
    double _value = 66;

    return _value;
}

int main() {

    double ret_value = 0;

    auto rf1 = std::async(std::launch::async, function1);
    auto status1 = rf1.wait_for(std::chrono::nanoseconds(1));

    auto rf2 = std::async(std::launch::async, function2);
    auto status2 = rf2.wait_for(std::chrono::nanoseconds(1));

    while (true) {

        if (status1 == std::future_status::ready) {
            std::cout << "RF1 FINISHED FIRST" << std::endl;

            // No longer need returned val from 2nd thread.
            // Get result from 1st thread
            ret_value = rf1.get();
            break;
        }
        else if (status2 == std::future_status::ready) {
            std::cout << "RF2 FINISHED FIRST" << std::endl;

            // No longer need returned val from 1st thread.
            // Get result from 2nd thread
            ret_value = rf2.get();
            break;
        }

        status1 = rf1.wait_for(std::chrono::nanoseconds(1));
        status2 = rf2.wait_for(std::chrono::nanoseconds(1));

    }

    // Do some calculations on the quickest
    // returned value
    double some_value = ret_value + 40;

    return 0;
}

The logic in your code inside the while will always call rf1.wait_for() until its status is ready.

Since your ready checks will break out of the loop, you do not need to use else if to decide to do further waiting. Just do the two waits again, like you did before you entered the while .

    status1 = rf1.wait_for(std::chrono::nanoseconds(1));
    status2 = rf2.wait_for(std::chrono::nanoseconds(1));

You have updated your question and you changed the behavior of the called functions away from the original behavior. Instead of hashing over the changes, let's talk about the problem in general terms.

  • You are attempting to wait at a nanosecond resolution.
  • Your threads as currently implemented only differ by 2 iterations of a fairly trivial loop body.
  • The compiler is free to optimize the code in such a way that the functions could execute in nearly the same amount of time.
  • So, a 1 nanosecond peek back and forth on the futures is not a reliable way to determine which future was actually returned first.

To resolve a close finish, you could use a buzzer, like they do in game shows. The first to buzz is clearly indicated by a signal. Instead of trying to implement a real buzzer, we can mimic one with a pipe. And, the function buzzes in by writing a value to the pipe.

int photo_finish[2];

double function1() {
    int id = 1;
    //...
    write(photo_finish[1], &id, sizeof(id));
    double _value = 66;
    return _value;
}

double function2() {
    int id = 2;
    //...
    write(photo_finish[1], &id, sizeof(id));
    double _value = 66;
    return _value;
}

The waiting code then reads from the pipe, and observes the value. The value indicates which function completed first, and so the code then waits on the appropriate future to get the value.

    pipe(photo_finish);

    auto rf1 = std::async(std::launch::async, function1);
    auto rf2 = std::async(std::launch::async, function2);

    int id = 0;
    read(photo_finish[0], &id, sizeof(id));

    if (id == 1) {
        auto status1 = rf1.wait();
        //...
    } else if (id == 2) {
        auto status2 = rf2.wait();
        //...
    }

Note that the sample code omits error handling for brevity.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM