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:
- Out of the two
threads
, to detect which one of them is first to return the value of66
- As soon as a thread returns
66
(regardless of if the other thread has completed), the returned value is then made available inmain()
for some further simple arithmetic to be performed upon it - If a thread returns
66
and arithmetic is performed upon this value inmain()
, and then the other thread later on delivers66
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:
- How to check if thread has finished work in C 11 and above?
- Using Multithreading two threads return same value with different inputs?
- C threads for background loading
- Future returned by a function
- 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;
}
CodePudding user response:
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.