0

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 (e.g. 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;
}
p.luck
  • 646
  • 2
  • 9
  • 34
  • 1
    For clarity, if you're familiar with it, this seems like you're looking for a C++ standard-lib equivalent of the winapi WaitForMultipleObjects. That is to say, you 'wait' on multiple notifiable contexts (in this case threads) and reap the first one you determine has signaled complete. Is that correct ? – WhozCraig Sep 16 '22 at 16:41
  • @WhozCraig Thanks for your response. This does sound similar to what I am looking for, yes. I am using `linux` however, so I don't believe this would be compatible - I have updated my question with the `linux` tag to address this. – p.luck Sep 16 '22 at 16:49
  • 1
    It certainly wouldn't be compatible. It was more an architecture-type question. That was an incredibly handy ability when I was deep in winapi programming, and was disappointed when I saw it wasn't included in at least a 'handy' vernacular in the standard threading/futures library. I can understand why, however. It has [been asked before](https://stackoverflow.com/questions/10818254/learning-threads-on-linux) here, multiple times in fact, whether such a facility exists. – WhozCraig Sep 16 '22 at 17:04

1 Answers1

1

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.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • Thank you for your answer. I have made an edit to my question- if possible, in terms of syntax was this what you meant? I have also added some `FOR` loops to both functions in a bid to determine which should finish first, but the function that should be slower (in theory) continues to print that it has finished first. – p.luck Sep 16 '22 at 17:23
  • When you change the question, the answer no longer applies. So I am forced to delete this answer, and probably won't try to answer your question, since it is a moving target ... – jxh Sep 16 '22 at 17:25
  • 2
    But generally, programs can be modeled with real life scenarios. If you are an observer of two people doing tasks independently, but you are only able to look at one person and then the other, how would you try to figure out which person finished their task fastest? – jxh Sep 16 '22 at 17:32
  • 1
    If you find a satisfactory solution in the real life model, you then translate the solution to your code. – jxh Sep 16 '22 at 17:34
  • Thanks again for your replies. For the edits made, the additional logic added is for debugging purposes to show the output of your suggestion, which produced the same/ similar output to those listed under the sub-heading “Problems and Current Output”, which could also satisfy “Q1”. As such, I think it would be useful for your answer to remain, if possible. – p.luck Sep 16 '22 at 19:12
  • Regarding a solution to a real-life model, I will attempt to tackle the problem from this perspective. Being new to multi-threading I haven’t had much luck with this yet, although I have seen a recurring topic surrounding ‘atomics’ which may help. If you have any opinions on this, it would be highly appreciated. – p.luck Sep 16 '22 at 19:15