Created
October 1, 2019 12:23
-
-
Save Indy9000/973b5a03c7a66aa9985646e29743d46d to your computer and use it in GitHub Desktop.
difference between threads and async
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// worker threads vs async/futures | |
// | |
// This demonstrates that for heavy compute that require multiple workers that | |
// send the results in a queue to the caller, using async/futures is | |
// much simpler than using worker threads | |
#include <iostream> | |
#include <map> | |
#include <vector> | |
#include <algorithm> | |
#include <thread> | |
#include <queue> | |
#include <mutex> | |
#include <condition_variable> | |
#include <atomic> | |
#include <future> | |
// our simulated heavy compute is a fibonacci computer | |
int fib(int n){ | |
int p0 = 1; int p1 = 1; | |
if (n==0 ||n ==1) { return 1;} | |
int pi = 0; | |
for(int i=2;i<=n;i++){ | |
pi = p1 + p0; | |
p0 = p1; | |
p1 = pi; | |
} | |
return pi; | |
} | |
// here we implement this strategy with worker threads | |
// simple pub sub | |
// we'll use fib for the pub (many) and sub (one) | |
void multi_fib(){ | |
std::vector<std::thread> threads; | |
std::queue<std::pair<int,int>> results; | |
std::condition_variable cv; | |
std::mutex m; | |
std::atomic<bool> done {false}; | |
// create worker threads that do the computation | |
// results are placed in a queue | |
for(int i=0;i<5;++i){ | |
threads.emplace_back( | |
std::thread([&](int j){ | |
auto result = fib(j*10); | |
{ | |
std::lock_guard<std::mutex> lg(m); | |
results.emplace(std::make_pair(j,result)); | |
cv.notify_one(); | |
} | |
}, i) | |
); | |
} | |
// create a listner thread and receive results | |
auto listner = std::thread([&](){ | |
while(!done){ | |
std::unique_lock<std::mutex> ul(m); | |
cv.wait(ul,[&](){return !results.empty();}); | |
// cv notifications are not queued, therefore | |
// we need to empty the queue when we get a signal | |
while(!results.empty()){ | |
auto result = results.front(); | |
results.pop(); | |
std::cout << result.first << "," << result.second << std::endl; | |
} | |
} | |
}); | |
// wait for all the worker threads to finish | |
std::cout << "waiting for threads to join\n"; | |
for(auto &t : threads){ | |
t.join(); | |
} | |
// signal the listner thread that it can finish | |
std::cout << "setting done\n"; | |
done = true; | |
// wait for the listner to finish | |
listner.join(); | |
std::cout << "exiting\n"; | |
} | |
// same strategy as above with async/futures | |
void async_fib(){ | |
// results are placed into this vector | |
std::vector<std::future<int>> results; | |
// create workers | |
for(int i=0;i<5;++i){ | |
results.emplace_back(std::async(fib,(i*10))); | |
} | |
// wait and get the results | |
for(auto & f:results){ | |
std::cout << f.get() << std::endl; | |
} | |
} | |
int main() { | |
multi_fib(); | |
async_fib(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment