Packaged_task ve async arasındaki fark nedir


135

C ++ 11'in iş parçacıklı modeliyle çalışırken, şunu fark ettim:

std::packaged_task<int(int,int)> task([](int a, int b) { return a + b; });
auto f = task.get_future();
task(2,3);
std::cout << f.get() << '\n';

ve

auto f = std::async(std::launch::async, 
    [](int a, int b) { return a + b; }, 2, 3);
std::cout << f.get() << '\n';

tam olarak aynı şeyi yapıyor gibi görünüyor. Ben koştum eğer büyük bir fark olabileceğini anlıyoruz std::asyncile std::launch::deferred, ancak bu durumda orada biridir?

Bu iki yaklaşım arasındaki fark nedir ve daha da önemlisi, hangi kullanım durumlarında birini diğerine tercih etmeliyim?

Yanıtlar:


162

Aslında az önce verdiğiniz örnek, oldukça uzun bir işlev kullanıyorsanız, örneğin

//! sleeps for one second and returns 1
auto sleep = [](){
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 1;
};

Paketlenmiş görev

A packaged_taskkendi başına başlamaz, onu çağırmanız gerekir:

std::packaged_task<int()> task(sleep);

auto f = task.get_future();
task(); // invoke the function

// You have to wait until task returns. Since task calls sleep
// you will have to wait at least 1 second.
std::cout << "You can see this after 1 second\n";

// However, f.get() will be available, since task has already finished.
std::cout << f.get() << std::endl;

std::async

Öte yandan, std::asyncile launch::asyncfarklı bir iş parçacığı görevi çalıştırmayı dener:

auto f = std::async(std::launch::async, sleep);
std::cout << "You can see this immediately!\n";

// However, the value of the future will be available after sleep has finished
// so f.get() can block up to 1 second.
std::cout << f.get() << "This will be shown after a second!\n";

sakınca

Ancak asyncher şey için kullanmayı denemeden önce , geri dönen geleceğin aşağıdakileri future::~futureengelleyen özel bir paylaşılan duruma sahip olduğunu unutmayın :

std::async(do_work1); // ~future blocks
std::async(do_work2); // ~future blocks

/* output: (assuming that do_work* log their progress)
    do_work1() started;
    do_work1() stopped;
    do_work2() started;
    do_work2() stopped;
*/

Dolayısıyla, gerçek eşzamansız olmasını istiyorsanız, geri gönderilenleri saklamanız gerekir futureveya koşullar değişirse sonucu önemsemiyorsanız:

{
    auto pizza = std::async(get_pizza);
    /* ... */
    if(need_to_go)
        return;          // ~future will block
    else
       eat(pizza.get());
}   

Bu konuda daha fazla bilgi için, Herb Sutter'ın makalesine bakın asyncve~future sorunu açıklar ve Scott Meyer std::futuresgelen std::asyncözel olmayan bilgiler tanımlayan,. Ayrıca, bu davranışın C ++ 14 ve üzeri sürümlerde belirtildiğini , ancak aynı zamanda C ++ 11'de de yaygın olarak uygulandığını unutmayın.

Diğer farklılıklar

Kullanarak std::asyncartık görevinizi belirli bir iş parçacığı üzerinde çalıştıramazsınız, burada std::packaged_taskbaşka iş parçacıklarına taşınabilir.

std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::thread myThread(std::move(task),2,3);

std::cout << f.get() << "\n";

Ayrıca, packaged_taskaramadan önce a'nın çağrılması gerekir f.get(), aksi takdirde gelecek asla hazır olmayacağından programınız donar:

std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);

TL; DR

Kullanım std::asyncEğer bazı şeyler yaptık ve onlar bitince gerçekten umurumda değil ve isterseniz std::packaged_taskdiğer parçacıkları taşıyın ya da geç onları aramak için şeyler kesmek istiyorum. Veya Christian'dan alıntı yapacak olursak :

Sonuçta a std::packaged_task, uygulama için sadece daha düşük seviyeli bir özelliktir std::async(bu nedenle std::async, diğer daha düşük seviyeli malzemelerle birlikte kullanılmasından daha fazlasını yapabilir std::thread). Basitçe söylenen a std::packaged_task, a'ya std::functionbağlıdır std::futureve std::asyncsarar ve a'yı çağırır std::packaged_task(muhtemelen farklı bir dizide).


9
Geleceğin yıkım sırasında eşzamansız bloklar tarafından döndürüldüğünü eklemelisiniz (get çağırmışsınız gibi), oysa packaged_task'tan dönen gelecek dönmez.
John5342

23
Sonuçta a std::packaged_task, uygulama için sadece daha düşük seviyeli bir özelliktir std::async(bu nedenle std::async, diğer daha düşük seviyeli malzemelerle birlikte kullanılmasından daha fazlasını yapabilir std::thread). Basitçe söylenen a std::packaged_task, a'ya std::functionbağlıdır std::futureve std::asyncsarar ve a'yı çağırır std::packaged_task(muhtemelen farklı bir dizide).
Christian Rau

~ Future () bloğunda bazı deneyler yapıyorum. Gelecekteki nesne imhası üzerindeki engelleme etkisini kopyalayamadım. Her şey eşzamansız olarak çalıştı. VS 2013 kullanıyorum ve async'i başlattığımda std :: launch :: async kullandım. VC ++ bu sorunu bir şekilde "düzeltti" mi?
Frank Liu

1
@FrankLiu: N3451, (bildiğim kadarıyla) C ++ 14'e giren kabul edilmiş bir teklif. Herb'ün Microsoft'ta çalıştığı göz önüne alındığında, bu özellik VS2013'te uygulanıyorsa şaşırmam. C ++ 11 kurallarını sıkı bir şekilde takip eden bir derleyici, bu davranışı yine de gösterecektir.
Zeta

1
@Mikhail Bu cevap hem C ++ 14 hem de C ++ 17'den önce geliyor, bu yüzden standartlara sahip değildim, sadece elimde teklifler var. Paragrafı kaldıracağım.
Zeta

1

Paketli Görev ve eşzamansız

p> Paketlenmiş görev, bir görev[function or function object]ve gelecek / vaat çifti içerir. Görev return ifadesi yerine getirdiğinde, bu nedenleriset_value(..)üzerindepackaged_taskbireyin vaadi.

a> Gelecek, söz ve paket görevi verildiğinde, iş parçacıkları hakkında çok fazla endişelenmeden basit görevler oluşturabiliriz [iş parçacığı sadece bir görevi yürütmek için verdiğimiz bir şeydir].

Ancak nasıl kullanılacağı birçok konu dikkate almak gerekir veya bir görev başlatıcısı olarak adlandırılan bir iş parçacığı tarafından ele alınabilir geçerli iş parçacığı üzerinde veya başka bir vs bulunmaktadır descisions en iyi çalışma olup olmadığı async()yeni bir iş parçacığı oluşturmak veya eski bir geri dönüşüm karar verir, bir veya basitçe görevi mevcut iş parçacığında çalıştırın. Bir gelecek döndürür.


0

"Sınıf şablonu std :: packaged_task, çağrılabilir herhangi bir hedefi (işlev, lambda ifadesi, bağlama ifadesi veya başka bir işlev nesnesi) eşzamansız olarak çağrılabilmesi için sarar. Döndürülen değeri veya istisnası, erişilebilen paylaşılan bir durumda saklanır std :: gelecek nesneler aracılığıyla. "

"Zaman uyumsuz şablon işlevi, f işlevini eşzamansız olarak (potansiyel olarak ayrı bir evrede) çalıştırır ve sonunda bu işlev çağrısının sonucunu tutacak bir std :: future döndürür."

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.