Giriş
Yeni C++ ile gelen std::async metodunu kullanarak verilen fonksiyonu bir başka thread içinde çalıştırabilme imkanı var. Şu dosyaları dahil ederiz.
Metod std::future döner. Parametre olarak lambda,metod artı lambda ve metodun kullanacağı parametreler geçilebiliyor.
lambda
lambda ve parametre olarak 2 ve 4 geçmek için şöyle yaparız.
std::asynch nesnenin metodunu çağırmak için std::bind kullanmaya ihtiyaç duymaz. Örnekte void metod sonuç dönüyor gibi görünse de çok takılmayın. Önemli olan çağrının olabildiğini görmek.
Metod future döndüğü için, thread'ler yaratıp, join ile beklemekten ve sonucu bir "out" parametresi ile almaya çalışmaktan çok daha kolay.
Bu metoda geçilebilen 3 launch policy parametresi var. Bunlar aşağıda.
1. launch::deferred
Verilen iş, future.get() ile sonucu istenince çalıştırılır.Bu durumda sonucu almaya çalışan thread kullanılmış olur. Aslında bu kullanım için iyi bir örnek düşünemiyorum. Açıklaması şöyle
2. launch::async
Örnek
Şu asynch metod çağrısı
Elimizde süre ölçen şöyle bir kod olsun.
Yeni C++ ile gelen std::async metodunu kullanarak verilen fonksiyonu bir başka thread içinde çalıştırabilme imkanı var. Şu dosyaları dahil ederiz.
#include <future>
#include <thread>
Metodun imzasıMetod std::future döner. Parametre olarak lambda,metod artı lambda ve metodun kullanacağı parametreler geçilebiliyor.
lambda
lambda ve parametre olarak 2 ve 4 geçmek için şöyle yaparız.
std::future<int> result(std::async([](int m,int n) {return m + n;} , 2, 4));
std::bindstd::asynch nesnenin metodunu çağırmak için std::bind kullanmaya ihtiyaç duymaz. Örnekte void metod sonuç dönüyor gibi görünse de çok takılmayın. Önemli olan çağrının olabildiğini görmek.
#include <future>
void A::first_fun()
{
auto future = std::async(&A::second_fun, this);
return future.get();
}
Niçin FutureMetod future döndüğü için, thread'ler yaratıp, join ile beklemekten ve sonucu bir "out" parametresi ile almaya çalışmaktan çok daha kolay.
const std::size_t n_async = 4;
const std::size_t N = 150;
std::array<std::future<double>, n_async> futures;
for (auto index = 0U; index < n_async; ++i) {
futures[i] = std::async(mycalculate, index * 10,index * 100);
}
double final_sum = 0.0;
for (auto&& future : futures) {
final_sum += future.get();
}
Şöyle yaparız.auto stuff = std::array<future_type, 2>
{
{
std::async(std::launch::async, func1),
std::async(std::launch::async, func2)
}
};
for (auto& f : stuff) f.wait();
Future nesnesinin destructor metodu
future nesnesi destructor'ı içinde thread'in bitmesini bekler.Bu yüzden bazı kodlarda dikkat etmek gerekir. Aşağıdaki kodda f future nesnesi arka planda çalışan foo metodu bitinceye kadar ana thread'i bloke eder.The destructor of the last future object associated with the asynchronous state of the returned std::future shall block until the future is ready.
{
auto f = std::async(std::launch::async, foo);
// dtor f::~f blocks until completion of foo()... why??
}
Benzer bir açıklama şöyle.{
std::async(std::launch::async, []{ f(); });
std::async(std::launch::async, []{ g(); }); // does not run until f() completes
}
Eğer fire and forget tarzı bir iş yapmak istiyorsak önümüzde iki seçenek var. Ya kendimiz thread açarız ya da bir thread pool kullanırız. Kendi thread'imizi şöyle açarız.thread([]{ f(); }).detach();
Kullanılan Policy TipleriBu metoda geçilebilen 3 launch policy parametresi var. Bunlar aşağıda.
1. launch::deferred
Verilen iş, future.get() ile sonucu istenince çalıştırılır.Bu durumda sonucu almaya çalışan thread kullanılmış olur. Aslında bu kullanım için iyi bir örnek düşünemiyorum. Açıklaması şöyle
std::launch::deferred indicates that the function call is to be deferred until either wait() or get() is called on the future.Açıklaması şöyle.
For std::launch:async the implementation supplies the thread that will preform the work while with std::launch::deferred you supply the thread (the one that reads the value).Şöyle yaparız.
X baz(X&);
auto f7 = std::async(std::launch::deferred, baz, std::ref(x)); //run in wait() or get()
//...
f7.wait(); //invoke deferred function
2. launch::async
Örnek
Şu asynch metod çağrısı
auto future = async (launch::async, []{ f(); });
// ...
future.wait();
aslında şöyle düşünülebilir.
Şöyle yaparız.
Bir sürü iş verip hepsinin sonucunu almak için şöyle yaparız. Bu kod aslında C#'taki Task.WhenAll() çağrısına denk gelir.
Bir sürü işi lambda olarak verip hepsinin sonucunu almak için şöyle yaparız.
Bu durumda fonksiyonun nasıl çalıştırılacağına c++ kütüphanesi karar veriyor. Açıklaması şöylethread t([]{ f(); });
// ...
t.join();
ÖrnekŞöyle yaparız.
int func()
{
...
}
int main()
{
auto result=std::async(std::launch::async, func);
...
}
ÖrnekBir sürü iş verip hepsinin sonucunu almak için şöyle yaparız. Bu kod aslında C#'taki Task.WhenAll() çağrısına denk gelir.
std::vector<std::string> names = { "file1.txt", "file2.txt", "file3.txt" };
std::vector<std::future<Foo>> results;
for (const auto& name : names) {
// load from the name file asynchronously
auto future = std::async(std::launch::async, &Foo::load, std::ref(name));
results.emplace_back(future);
}
// gather result
for (auto& future : results) {
Foo& data = future.get();
// todo use data from the file object
}
ÖrnekBir sürü işi lambda olarak verip hepsinin sonucunu almak için şöyle yaparız.
auto lambda = [&] {
...
};
std::vector<decltype(std::async(std::launch::async, lambda))> results;
int threadCount = ...;
for (int i = 0; i < threadCount; i++) {
results.emplace_back(std::move(std::async(std::launch::async, lambda)));
}
Visual C++ bu policy parametresi için yeni bir thread yaratmak yerine havuzu kullandığı için şikayet var. Açıklaması şöyleVisual C++ uses the Windows thread pool (Vista's CreateThreadpoolWork if available and QueueUserWorkItem if not) when calling std::async with std::launch::async.3. herhangi bir launch policy vermemek - default behavior
The number of threads in the pool is limited. If create several tasks that run for a long time without sleeping (including doing I/O), the upcoming tasks in the queue won't get a chance to work.
Behaves the same as async(std::launch::async | std::launch::deferred, f, args...). In other words, f may be executed in another thread or it may be run synchronously when the resulting std::future is queried for a value.Şöyle yaparız
auto future = std::async([]{...});
std::asynch ve performansElimizde süre ölçen şöyle bir kod olsun.
template <class Clock = std::chrono::high_resolution_clock, class Task>
double timing(Task&& t, typename std::result_of<Task()>::type* r = nullptr)
{
using namespace std::chrono;
auto begin = Clock::now();
if (r != nullptr) *r = std::forward<Task>(t)();
auto end = Clock::now();
return duration_cast<duration<double>>(end - begin).count();
}
İş yapan metod olsun.template <typename Num>
double asum(const std::vector<Num>& v, const std::size_t l, const std::size_t h)
{
...
return ...;
}
Çağırmak için şöyle yaparız.std::cout << 1000 * timing([&]() -> double { return asum(...); }, &r)
<< " msec " << r << std::endl;