Aracılığıyla paylaş


Görev Parallelliği (Eşzamanlılık Çalışma Zamanı)

Eşzamanlılık Çalışma Zamanı bir görev, belirli bir işi yapan ve genellikle diğer görevlerle paralel çalışan iş birimidir.Görev, Görev grubu öğesini düzenleyen ince detaylı, ek görevlere ayrılabilir.

Zaman uyumsuz kod yazar ve bazı işlemlerin zaman uyumsuz işlem tamamlandıktan sonra gerçekleştirilmesini isterseniz, görevleri kullanırsınız.Örneğin bir görevi bir dosyadan zaman uyumsuz bir okuma yapmak için kullanabilir va ardından kullanılabilir hale geldikten sonra veriyi işlemek için başka bir görev—bu belgede daha sonra anlatılan bir devam görevi kullanabilirsiniz.Buna karşılık, paralel işleri küçük parçalara ayırmak için görev gruplarını kullanabilirsiniz.Örneğin kalan işi iki bölüme ayıran bir önyinelemeli algoritmanız olduğunu varsayın.Bu bölümleri aynı anda çalıştırmak için görev gruplarını kullanabilir, sonra da bölünmüş işin tamamlanmasını bekleyebilirsiniz.

İpucuİpucu

Koleksiyonun her öğesine paralel olarak aynı yordamı uygulamak istediğinizde, görev veya görev grubu yerine concurrency::parallel_for gibi bir paralel algoritma kullanın.Paralel algoritmalar hakkında daha fazla bilgi için bkz. Paralel Algoritmalar.

Önemli Noktalar

  • Başvuruya göre lambda ifadesine değişkenleri geçirirken, söz konusu değişkenin kullanım ömrünün görev bitene kadar sona ermeyeceğini güvence altına almalısınız.

  • Zaman uyumsuz kod yazdığınızda görevleri kullanın (concurrency::task sınıfı).

  • Paralel işi küçük parçalara ayırmak ve daha sonra bu tamamlamak amacıyla bu küçük parçaları beklemek istediğinizde görev gruplarını (concurrency::task_group sınıfı veya concurrency::parallel_invoke algoritması) kullanın.

  • Devamlılığını oluşturmak için concurrency::task::then yöntemini kullanın.Devamlılık başka bir görev tamamlandıktan sonra zaman uyumsuz olarak çalışan görevdir.Zaman uyumsuz iş zinciri oluşturmak için herhangi bir sayıda devamlılığa bağlanabilirsiniz.

  • Öncül görev tamamlandığında, öncül görev iptal edildiğinde veya özel bir durum oluştuğunda görev tabanlı devamlılık her zaman yürütme için zamanlanır.

  • Bir dizi görevin her üyesi tamamlandıktan sonra tamamlanan bir görev oluşturmak için concurrency::when_all kullanın.Bir dizi görevin bir üyesi tamamlandıktan sonra tamamlanan bir görev oluşturmak için concurrency::when_any kullanın.

  • Görevler ve görev grupları, Paralel Desen kitaplığı (PPL) iptal mekanizması içinde yer alabilirler.Daha fazla bilgi için bkz. PPL'de İptal.

  • Çalışma zamanının görevler ve görev grupları tarafından oluşturulan özel durumları nasıl işlediğini öğrenmek için, bkz. Eşzamanlılık Çalışma Zamanında Özel Durum İşleme.

Bu Belgede

  • Lambda İfadeleri kullanma

  • Görev Sınıfı

  • Devamlılık Görevleri

  • Değer Tabanlı Devamlılığa karşı Görev Tabanlı Devamlılık

  • Görevleri Oluşturma

    • When_all İşlevi

    • When_any İşlevi

  • Geciken Görevi Yürütme

  • Görev Grupları

  • task_group'u structured_task_group ile Karşılaştırma

  • Örnek

  • Güçlü Programlama

Lambda İfadeleri kullanma

Kısa sözdizimleri nedeniyle, lambda ifadeleri görevler ve görev grupları tarafından gerçekleştirilen işi tanımlamanın ortak yoludur.İşte bazı kullanım ipuçları:

  • Görevler genellikle arka plan iş parçacıklarında çalıştığından lambda ifadelerinde değişkenleri yakaladığınızda nesne yaşam süresine dikkat edin.Değerine göre bir değişken yakaladığınızda, lambda gövdesinde o değişkenin bir kopyası yapılır.Başvuruya göre yakaladığınızda, kopya yapılmaz.Bu nedenle, başvuruya göre yakaladığınız herhangi bir değişkenin kullanım ömrünün onu kullanan görevin kullanım ömründen fazla olduğundan emin olun.

  • Göreve bir lambda ifadesi geçirirken, başvuruya göre yığında ayrılmış değişkenleri yakalamayın.

  • Lambda ifadelerinde yakaladığınız değişkenler ile ilgili olarak açık olduğunuz takdirde değere göre ve başvuruya göre ne yakalayacağınızı belirleyebilirsiniz.Bu nedenle lambda ifadeleri için [=] ya da [&] seçeneklerini kullanmamanızı öneririz.

Devam eden bir zincirde bir görev bir değişkene atandığında ve başka bir görev bu değişkeni okuduğunda söz konusu olan ortak bir modeldir.Her devamlılık görevinin değişkene ait farklı bir kopyasını alması nedeniyle değere göre alamazsınız.Yığın ayırma değişkenlerinde ayrıca başvuruya göre yakalama yapamazsınız çünkü değişken artık geçerli olmayabilir.

Bu sorunu çözmek için, değişkeni kaydıracak akıllı işaretçiyi değerine göre geçirecek std::shared_ptr gibi akıllı bir işaretçi kullanın.Bu şekilde, arka plandaki nesneye okunabilirlik atanabilir ve onu kullanan görevlerden daha uzun sürmesi sağlanır.Değişken bir işaretçi veya başvuru sayılı tutamaç olsa bile bu tekniği (^) bir Windows çalışma zamanı nesnesi olarak kullanın.İşte basit bir örneği:

// lambda-task-lifetime.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
#include <string>

using namespace concurrency;
using namespace std;

task<wstring> write_to_string()
{
    // Create a shared pointer to a string that is  
    // assigned to and read by multiple tasks. 
    // By using a shared pointer, the string outlives 
    // the tasks, which can run in the background after 
    // this function exits.
    auto s = make_shared<wstring>(L"Value 1");

    return create_task([s] 
    {
        // Print the current value.
        wcout << L"Current value: " << *s << endl;
        // Assign to a new value.
        *s = L"Value 2";

    }).then([s] 
    {
        // Print the current value.
        wcout << L"Current value: " << *s << endl;
        // Assign to a new value and return the string.
        *s = L"Value 3";
        return *s;
    });
}

int wmain()
{
    // Create a chain of tasks that work with a string.
    auto t = write_to_string();

    // Wait for the tasks to finish and print the result.
    wcout << L"Final value: " << t.get() << endl;
}

/* Output:
    Current value: Value 1
    Current value: Value 2
    Final value: Value 3
*/

Lambda ifadeleri hakkında daha fazla bilgi için bkz. C++'deki Lambda İfadeleri.

[Üst]

Görev Sınıfı

Görevleri bağımsız işlemler kümesi olarak oluşturmak için concurrency::task sınıfını kullanabilirsiniz.Bu bileşim modeli devamlılıklar kavramı tarafından desteklenir.Devamlılık, önceki veya öncül görev tamamlandığında kodun yürütülebilmesini sağlar.Öncül görevin sonucu bir veya daha fazla devamlılık görevine giriş olarak geçirilir.Öncül görev tamamladığında, burada bekleyen herhangi bir devamlılık görevi yürütülmek üzere zamanlanır.Her devamlılık görevi, öncül görevin sonucunun bir kopyasını alır.Sırasıyla bu süreklilik görevleri başka süreklilikler için öncül görevler olabilir, bu şekilde bir görev zinciri oluşturulur.Devamlılıklar, aralarında belirli bir tutarlılığa sahip rastgele uzunluktaki görev zincirleri oluşturmanıza yardımcı olur.Ayrıca, görev başlamadan önce ya da çalışırken birlikte çalışılabilir bir durumdayken, görev bir iptal işlemine katılabilir.Bu iptal etme modeli hakkında daha fazla bilgi için bkz. PPL'de İptal.

task bir şablon sınıfıdır.Tür parametresi T, görev tarafından üretilen sonucun türüdür.Görev bir değer döndürmezse bu tür void olabilir.T, const değiştiriciyi kullanamaz.

Görev oluşturduğunuzda, görevin gövdesini gerçekleştiren bir iş işlevi sağlarsınız.Bu iş işlevi bir lambda işlevi, işlev işaretçisi veya işlev nesnesi biçiminde gelir.Sonuç almadan bitirilecek görevi beklemek için concurrency::task::wait yöntemini arayın.task::wait yöntemi, görevin tamamlandığını veya iptal edildiğini belirten bir concurrency::task_status değeri döndürür.Görevin sonucunu almak için, concurrency::task::get yöntemini çağırın.Bu yöntem task::wait çağırarak görevin bitmesini bekler ve bu nedenle sonuç kullanılabilir duruma gelene kadar geçerli iş parçacığının yürütülmesini engeller.

Aşağıdaki örnek, bir görevin nasıl oluşturulduğunu, sonucunun beklendiğini ve değerinin görüntülendiğini gösterir.Bu belgedeki örneklerde, daha kısa sözdizimleri sağladığından lambda işlevleri kullanılmıştır.Ancak ayrıca görevleri kullanırken işlev işaretçilerini ve işlev nesnelerini de kullanabilirsiniz.

// basic-task.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Create a task.
    task<int> t([]()
    {
        return 42;
    });

    // In this example, you don't necessarily need to call wait() because 
    // the call to get() also waits for the result.
    t.wait();

    // Print the result.
    wcout << t.get() << endl;
}

/* Output:
    42
*/

concurrency::create_task işlevini kullanırken, türü bildirmek yerine auto anahtar sözcüğünü kullanabilirsiniz.Örneğin bu kodun kimlik matrisi oluşturduğunu ve yazdırdığını düşünün:

// create-task.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <string>
#include <iostream>
#include <array>

using namespace concurrency;
using namespace std;

int wmain()
{
    task<array<array<int, 10>, 10>> create_identity_matrix([]
    {
        array<array<int, 10>, 10> matrix;
        int row = 0;
        for_each(begin(matrix), end(matrix), [&row](array<int, 10>& matrixRow) 
        {
            fill(begin(matrixRow), end(matrixRow), 0);
            matrixRow[row] = 1;
            row++;
        });
        return matrix;
    });

    auto print_matrix = create_identity_matrix.then([](array<array<int, 10>, 10> matrix)
    {
        for_each(begin(matrix), end(matrix), [](array<int, 10>& matrixRow) 
        {
            wstring comma;
            for_each(begin(matrixRow), end(matrixRow), [&comma](int n) 
            {
                wcout << comma << n;
                comma = L", ";
            });
            wcout << endl;
        });
    });

    print_matrix.wait();
}
/* Output:
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0
    0, 1, 0, 0, 0, 0, 0, 0, 0, 0
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0
    0, 0, 0, 1, 0, 0, 0, 0, 0, 0
    0, 0, 0, 0, 1, 0, 0, 0, 0, 0
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0
    0, 0, 0, 0, 0, 0, 1, 0, 0, 0
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0
    0, 0, 0, 0, 0, 0, 0, 0, 1, 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 1
*/

Eşdeğer işlem oluşturmak için create_task işlevini kullanabilirsiniz.

auto create_identity_matrix = create_task([]
{
    array<array<int, 10>, 10> matrix;
    int row = 0;
    for_each(begin(matrix), end(matrix), [&row](array<int, 10>& matrixRow) 
    {
        fill(begin(matrixRow), end(matrixRow), 0);
        matrixRow[row] = 1;
        row++;
    });
    return matrix;
});

Görev yürütme sırasında bir özel durum atılırsa, çalışma zamanı sonraki çağrısında bu özel durumu task::get veya task::wait ya da görev tabanlı sürekliliğe devreder.Görev özel durum işleme mekanizması hakkında daha fazla bilgi için bkz. Eşzamanlılık Çalışma Zamanında Özel Durum İşleme.

task, concurrency::task_completion_event, iptal kullanan bir örnek için bkz. İzlenecek yol: Görevleri ve XML HTTP İsteklerini Kullanarak Bağlanma. (task_completion_event sınıfı bu belgede daha sonra tanımlanmıştır.)

İpucuİpucu

Windows Mağazası uygulamalarındaki görevlere özgü ayrıntıları öğrenmek için, bkz. Asynchronous programming in C++ ve Windows Mağazası Uygulamaları için C++ Uygulamasında Zaman Uyumsuz İşlemler Oluşturma.

[Üst]

Devamlılık Görevleri

Zaman uyumsuz programlama, ikinci bir işlem başlatmak ve ona veri aktarmak için tamamlandığında, bir zaman uyumsuz işlem için çok yaygın değildir.Geleneksel olarak, bu geri arama yöntemleri kullanılarak yapılmıştır.Eşzamanlılık Çalışma Zamanı içinde aynı işlevler devamlılık görevleri tarafından sağlanır.Devamlılık görevi (sadece devamlılık olarak da adlandırılır), the öncül olarak da bilinen başka bir görev tarafından öncül görev tamamlandıktan sonra çalıştırılan zaman uyumsuz bir görevdir.Devamlılıkları kullanarak şunları yapabilirsiniz:

  • Öncülden devama veri geçir.

  • Devamlılığın çağrıldığı ya da çağrılmadığı kesin koşulları belirtin.

  • Başlamadan önce veya çalışırken işbirliği içerisinde devamlılığı iptal edin.

  • Devamlılığın nasıl zamanlanması gerektiği hakkında ipuçları sağlayın. (Bu yalnızca Windows Mağazası uygulamaları için geçerlidir.Daha fazla bilgi için bkz. Windows Mağazası Uygulamaları için C++ Uygulamasında Zaman Uyumsuz İşlemler Oluşturma.)

  • aynı öncülden gelen birden çok devamlılığını çağırır.

  • Öncüllerin tümü veya herhangi biri tamamlandığında bir devamlılığı çağırır.

  • Birbiri ardına gelen herhangi bir uzunluktaki zincir devamlılıkları.

  • Öncülün attığı özel durumları işlemek için devamlılığı kullanın.

Bu özellikler, ilk görevi tamamladığında bir veya daha fazla görevi yürütmenize olanak verir.Örneğin ilk görev bir dosyayı diskten okuduktan sonra o dosyayı sıkıştıran bir devam oluşturabilirsiniz.

Aşağıdaki örnekte, önceki örnek, kullanılabildiğinde öncül görevini değerini yazan bir devamlılık zamanlamak üzere concurrency::task::then yöntemini kullanacak şekilde değiştirilmiştir.

// basic-continuation.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t = create_task([]() -> int
    {
        return 42;
    });

    t.then([](int result)
    {
        wcout << result << endl;
    }).wait();

    // Alternatively, you can chain the tasks directly and 
    // eliminate the local variable. 
    /*create_task([]() -> int
    {
        return 42;
    }).then([](int result)
    {
        wcout << result << endl;
    }).wait();*/
}

/* Output:
    42
*/

Görevleri art arda veya iç içe yapabilirsiniz.Görev ayrıca birden çok devamlılığa sahip olabilir.Aşağıdaki örnek, önceki görevin değerini üç kez arttıran bir temel devamlılık zincirini göstermektedir.

// continuation-chain.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t = create_task([]() -> int
    { 
        return 0;
    });

    // Create a lambda that increments its input value.
    auto increment = [](int n) { return n + 1; };

    // Run a chain of continuations and print the result. 
    int result = t.then(increment).then(increment).then(increment).get();
    wcout << result << endl;
}

/* Output:
    3
*/

Devamlılık başka bir görevi de döndürebilir.İptal yoksa bu görev sonraki devamdan önce yürütülür.Bu teknik zaman uyumsuz çözülme olarak bilinir.Zaman uyumsuz çözülme arka planda ek çalışma gerçekleştirmek istediğinizde, ancak geçerli görevin geçerli iş parçacığını engellemesini istemiyorsanız yararlıdır. (Bu, UI iş parçacığında çalışabilen devamlılığın söz konusu olduğu yerde Windows Mağazası uygulamalarında yaygındır).Aşağıdaki örnek, üç görev gösterir.İlk görev bir devamlılık görevinden önce çalıştırılan başka bir görevi döndürür.

// async-unwrapping.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t = create_task([]()
    {
        wcout << L"Task A" << endl;

        // Create an inner task that runs before any continuation 
        // of the outer task. 
        return create_task([]()
        {
            wcout << L"Task B" << endl;
        });
    });

    // Run and wait for a continuation of the outer task.
    t.then([]()
    {
        wcout << L"Task C" << endl;
    }).wait();
}

/* Output:
    Task A
    Task B
    Task C
*/
Önemli notÖnemli

İç içe geçmiş N türünden bir görev oluşan görev task<N> türünden değil, N türünden olur; iç içe görev tamamlandığında da tamamlanır.Başka bir deyişle, süreklilik iç içe geçmiş görevin çözülmesini gerçekleştirir.

[Üst]

Değer Tabanlı Devamlılığa karşı Görev Tabanlı Devamlılık

Geri dönüş türü T olan bir task nesnesi düşünüldüğünde, bunun devam görevlerine T ya da task<T> türü bir değer sağlayabilirsiniz.Değer tabanlı devamlılık olarak da bilinen T türünü alan devamlılık.Öncül görev hatasız olarak tamamlandığında veya iptal edilmediğinde değer tabanlı devamlılık yürütmek için zamanlanır.Görev tabanlı devamlılık olarak da bilinen, parametre olarak task<T> türünü alan devamlılık.Öncül görev tamamlandığında, öncül görev iptal edildiğinde veya özel bir durum oluştuğunda görev tabanlı devamlılık her zaman yürütme için zamanlanır.Öncül görevin sonucunu elde etmek için daha sonra task::get arayabilirsiniz.Öncül görev iptal edildi ise, task::get şunu atar: concurrency::task_canceled.Öncül görev özel durum attıysa, task::get bu özel durumu yeniden atar.Öncül görev iptal edildiğinde görev tabanlı devamlılık iptal edildi olarak işaretlenmedi.

[Üst]

Görevleri Oluşturma

Bu bölümde genel desenleri uygulamak için birden çok görev oluşturmanıza yardımcı olabilecek concurrency::when_all ve concurrency::when_any işlevleri açıklanmaktadır.

When_all İşlevi

when_all işlevi bir dizi görev tamamlandıktan sonra tamamlanan bir görev oluşturur.Bu işlev kümedeki her görevin sonucunu içeren bir std::vector nesnesi döndürür.Aşağıdaki basit örnekte, üç başka görevin tamamlandığını belirten bir görev oluşturmak için when_all kullanılmaktadır.

// join-tasks.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <array>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Start multiple tasks. 
    array<task<void>, 3> tasks = 
    {
        create_task([] { wcout << L"Hello from taskA." << endl; }),
        create_task([] { wcout << L"Hello from taskB." << endl; }),
        create_task([] { wcout << L"Hello from taskC." << endl; })
    };

    auto joinTask = when_all(begin(tasks), end(tasks));

    // Print a message from the joining thread.
    wcout << L"Hello from the joining thread." << endl;

    // Wait for the tasks to finish.
    joinTask.wait();
}

/* Sample output:
    Hello from the joining thread.
    Hello from taskA.
    Hello from taskC.
    Hello from taskB.
*/

[!NOT]

when_all öğesine geçirdiğiniz görevler tekdüzen olmalıdır.Başka bir deyişle, bunların tümü aynı türü döndürmelidir.

Aşağıdaki örnekte gösterildiği gibi, bir dizi görev tamamlandıktan sonra tamamlanan görevi oluşturmak için de && sözdizimini kullanabilirsiniz.

auto t = t1 && t2; // same as when_all

Bir sürekliliği when_all ile kullanarak görev kümesindeki görevler tamamlandıktan sonra eylemi gerçekleştirmek yaygındır.Aşağıdaki örnek, her biri bir int sonucu üreten üç görevin toplamını yazdırmak için oluşturulmuş, önceki örneğinin değiştirilmiş bir şeklidir.

// Start multiple tasks. 
array<task<int>, 3> tasks =
{
    create_task([]() -> int { return 88; }),
    create_task([]() -> int { return 42; }),
    create_task([]() -> int { return 99; })
};

auto joinTask = when_all(begin(tasks), end(tasks)).then([](vector<int> results)
{
    wcout << L"The sum is " 
          << accumulate(begin(results), end(results), 0)
          << L'.' << endl;
});

// Print a message from the joining thread.
wcout << L"Hello from the joining thread." << endl;

// Wait for the tasks to finish.
joinTask.wait();

/* Output:
    Hello from the joining thread.
    The sum is 229.
*/

Bu örnekte, görev tabanlı süreklilik üretmek için task<vector<int>> öğesi de belirtebilirsiniz.

Görev dizisindeki herhangi bir görev iptal edilir ya da bir istisna atarsa, when_all hemen tamamlanır ve kalan görevlerin bitmesi için beklemez.Bir özel durum oluşursa, task::get veya task::wait çağırdığınızda çalışma zamanı özel durumu when_all görev nesnesine yeniden atar.Birden fazla görev atılırsa, çalışma zamanı bunlardan birini seçer.Bu nedenle, tüm görevleri tamamladıktan sonra tüm özel durumları dikkate aldığınızdan emin olun; işlenmemiş bir görev özel durumu uygulamanın sonlandırılmasına neden olur.

İşte programınızın tüm özel durumları gözlemlemesini sağlamak için kullanabileceğiniz bir yardımcı program işlevi.Sunulan aralıktaki her görev için observe_all_exceptions, oluşmuş bir özel durumun yeniden atılmasını tetikler ve ardından o özel durumu bastırır.

// Observes all exceptions that occurred in all tasks in the given range. 
template<class T, class InIt> 
void observe_all_exceptions(InIt first, InIt last) 
{
    std::for_each(first, last, [](concurrency::task<T> t)
    {
        t.then([](concurrency::task<T> previousTask)
        {
            try
            {
                previousTask.get();
            }
            // Although you could catch (...), this demonstrates how to catch specific exceptions. Your app 
            // might handle different exception types in different ways. 
            catch (Platform::Exception^)
            {
                // Swallow the exception.
            }
            catch (const std::exception&)
            {
                // Swallow the exception.
            }
        });
    });
}

C++ ve XAML kullanan ve diske dosyalar kümesi yazan Windows Mağazası uygulaması düşünün.Aşağıdaki örnek, programın tüm özel durumlara uymasını sağlamak için when_all ve observe_all_exceptions öğesinin nasıl kullanılacağını gösterir.

// Writes content to files in the provided storage folder. 
// The first element in each pair is the file name. The second element holds the file contents.
task<void> MainPage::WriteFilesAsync(StorageFolder^ folder, const vector<pair<String^, String^>>& fileContents)
{
    // For each file, create a task chain that creates the file and then writes content to it. Then add the task chain to a vector of tasks.
    vector<task<void>> tasks;
    for (auto fileContent : fileContents)
    {
        auto fileName = fileContent.first;
        auto content = fileContent.second;

        // Create the file. The CreationCollisionOption::FailIfExists flag specifies to fail if the file already exists.
        tasks.emplace_back(create_task(folder->CreateFileAsync(fileName, CreationCollisionOption::FailIfExists)).then([content](StorageFile^ file)
        {
            // Write its contents. 
            return create_task(FileIO::WriteTextAsync(file, content));
        }));
    }

    // When all tasks finish, create a continuation task that observes any exceptions that occurred. 
    return when_all(begin(tasks), end(tasks)).then([tasks](task<void> previousTask)
    {
        task_status status = completed;
        try
        {
            status = previousTask.wait();
        }
        catch (COMException^ e)
        {
            // We'll handle the specific errors below.
        }
        // TODO: If other exception types might happen, add catch handlers here. 

        // Ensure that we observe all exceptions.
        observe_all_exceptions<void>(begin(tasks), end(tasks));

        // Cancel any continuations that occur after this task if any previous task was canceled. 
        // Although cancellation is not part of this example, we recommend this pattern for cases that do. 
        if (status == canceled)
        {
            cancel_current_task();
        }
    });
}

Bu örneği çalıştırmak için

  1. MainPage.xaml içinde bir Button denetimi ekleyin.

    <Button x:Name="Button1" Click="Button_Click">Write files</Button>
    
  2. MainPage.xaml.h içinde bu düz bildirimleri, MainPage sınıf bildiriminin private bölümüne ekleyin.

    void Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
    concurrency::task<void> WriteFilesAsync(Windows::Storage::StorageFolder^ folder, const std::vector<std::pair<Platform::String^, Platform::String^>>& fileContents);
    
  3. MainPage.xaml.cpp içinde Button_Click olay işleyicisini uygulayın.

    // A button click handler that demonstrates the scenario. 
    void MainPage::Button_Click(Object^ sender, RoutedEventArgs^ e)
    {
        // In this example, the same file name is specified two times. WriteFilesAsync fails if one of the files already exists.
        vector<pair<String^, String^>> fileContents;
        fileContents.emplace_back(make_pair(ref new String(L"file1.txt"), ref new String(L"Contents of file 1")));
        fileContents.emplace_back(make_pair(ref new String(L"file2.txt"), ref new String(L"Contents of file 2")));
        fileContents.emplace_back(make_pair(ref new String(L"file1.txt"), ref new String(L"Contents of file 3")));
    
        Button1->IsEnabled = false; // Disable the button during the operation.
        WriteFilesAsync(ApplicationData::Current->TemporaryFolder, fileContents).then([this](task<void> previousTask)
        {
            try
            {
                previousTask.get();
            }
            // Although cancellation is not part of this example, we recommend this pattern for cases that do. 
            catch (const task_canceled&)
            {
                // Your app might show a message to the user, or handle the error in some other way.
            }
    
            Button1->IsEnabled = true; // Enable the button.
        });
    }
    
  4. MainPage.xaml.cpp içinde örnekte gösterildiği gibi WriteFilesAsync uygulayın.

İpucuİpucu

when_all, sonucu olarak task üreten, engelleyici olmayan bir işlevdir.task::wait seçeneğinden farklı olarak, ASTA (ASTA uygulaması) iş parçacığında Windows Mağazası uygulamasından bu işlevi çağırmak güvenlidir.

[Üst]

When_any İşlevi

when_any işlevi, bir görev kümesindeki ilk görev tamamlandığında tamamlanan bir görev oluşturur.Bu işlev, tamamlanmış bir görevin sonucunu ve bu görevin kümedeki dizinini içeren bir std::pair nesnesi döndürür.

when_any işlevi özellikle aşağıdaki senaryolarda yararlıdır:

  • Fazlalık işlemlerBirçok şekilde gerçekleştirilen bir algoritma veya işlem düşünün.Önce sona eren işlemi seçmek, sonra da kalan işlemleri iptal etmek için when_any işlevini seçebilirsiniz.

  • Dönüşümlü işlemlerTümünün bitmesi gereken birden çok işlem başlatabilir ve her işlem bittiğinde sonuçları işlemek için when_any işlevini kullanabilirsiniz.Bir işlem tamamlandıktan sonra bir veya daha fazla ek görev başlatabilirsiniz.

  • Daraltılmış işlemler.Eşzamanlı işlemlerin sayısını sınırlayarak önceki senaryoyu genişletmek için when_any işlevini kullanabilirsiniz.

  • Süresi dolan işlemleri.Bir veya daha fazla görevin arasından seçim yapmak veya belirli bir süre sonra biten bir görev seçmek için when_any işlevini kullanabilirsiniz.

when_all öğesinde olduğu gibi, görev kümesindeki ilk görev sona erdiğinde eylemi gerçekleştirmek için when_any öğesine sahip devamlılık yaygın olarak kullanılır.Aşağıdaki basit örnekte, üç başka görevden birincisini tamamlayan bir görevi oluşturmak için when_any kullanılmaktadır.

// select-task.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <array>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Start multiple tasks. 
    array<task<int>, 3> tasks = {
        create_task([]() -> int { return 88; }),
        create_task([]() -> int { return 42; }),
        create_task([]() -> int { return 99; })
    };

    // Select the first to finish.
    when_any(begin(tasks), end(tasks)).then([](pair<int, size_t> result)
    {
        wcout << "First task to finish returns "
              << result.first
              << L" and has index "
              << result.second
              << L'.' << endl;
    }).wait();
}

/* Sample output:
    First task to finish returns 42 and has index 1.
*/

Bu örnekte, görev tabanlı devam üretmen için, task<pair<int, size_t>> öğesi de belirtebilirsiniz.

[!NOT]

when_all öğesinde olduğu gibi when_any öğesine geçirdiğiniz görevler aynı türü geri döndürür.

Aşağıdaki örnekte gösterildiği gibi, bir dizi görevin ilk görevi tamamlandıktan sonra tamamlanan görevi oluşturmak için de || sözdizimini kullanabilirsiniz.

auto t = t1 || t2; // same as when_any

İpucuİpucu

when_all öğesinde olduğu gibi, when_any engellemez ve ASTA iş parçacığı üzerinde Windows Mağazası uygulamasını çağırmak güvenlidir.

[Üst]

Geciken Görevi Yürütme

Bir koşul sağlanana veya dış bir olaya yanıt olarak bir görevin başlatılmasına kadar bir görevin yürütülmesini geciktirmek bazen gereklidir.Örneğin zaman uyumsuz programlamada bir görevi bir G/Ç tamamlama olayına yanıt olarak başlatmanız gerekebilir.

Bunu gerçekleştirmenin iki yolu devamlılığı kullanmak veya bir görevi başlatıp görevin çalışma işlevi içindeki olayda beklemektir.Ancak bu tekniklerden birinin kullanılamayacağı durumlar vardır.Örneğin bir devam oluşturmak için öncül göreve sahip olmalısınız.Ancak öncül göreviniz yoksa bir görev tamamlama olayı oluşturabilir ve ardından tamamlama olayının kullanılabilir olduğunda öncül göreve zincirleyebilirsiniz.Ayrıca, bekleyen görev zaten iş parçacığını engelleyeceğinden, senkronize olmayan bir işlem tamamlandığında çalışmayı gerçekleştirmek için görev tamamlama etkinliğini kullanabilir, böylece iş parçacığını serbest kalmasını sağlayabilirsiniz.

concurrency::task_completion_event sınıfı bu tür görev bileşimlerini basitleştirmeye yardımcı olur.task sınıfı gibi, tür parametresi T görev tarafından üretilen sonucun türüdür.Görev bir değer döndürmezse bu tür void olabilir.T, const değiştiriciyi kullanamaz.Genellikle, bir task_completion_event nesnesi, bir iş parçacığı veya bu değeri onun için kullanılabilir olduğunda sinyal veren görev sağlanır.Aynı zamanda bir veya daha fazla görev bu olayın dinleyicileri olarak ayarlanır.Olay ayarlandığında, dinleyici görevleri tamamlayacak ve bunların devamlılığını çalıştıracak şekilde zamanlanır.

Bir gecikmeden sonra tamamlanan bir görevi uygulamak için task_completion_event kullanan bir örnek için bkz. Nasıl yapılır: Bir Gecikmeden Sonra Tamamlanan bir Görev Oluşturma.

[Üst]

Görev Grupları

Görev grubu görev koleksiyonunu düzenler.Görev grupları, görevleri işi kaplayan bir sıraya iter.Zamanlayıcı görevleri bu sıradan çıkarır ve onları kullanılabilir bilgi işlem kaynaklarında yürütür.Bir görev grubuna görevler ekledikten sonra tüm görevlerin tamamlanmasını bekleyebilir veya henüz başlamamış görevleri iptal edebilirsiniz.

PPL, görev gruplarını temsil etmek için concurrency::task_group ve concurrency::structured_task_group sınıflarını, bu gruplarda çalışan görevleri temsil etmek için ise concurrency::task_handle sınıfını kullanır.task_handle sınıfı, işi gerçekleştiren kod içinde saklanır.task sınıfı gibi, iş işlevi lambda işlevi, işlev işaretçisi veya işlev nesnesi biçiminde gelir.Normalde task_handle nesneleriyle doğrudan çalışmanız gerekmez.Bunun yerine, iş işlevlerini bir görev grubuna geçirirsiniz ve bu görev grubu task_handle nesneleri oluşturur ve yönetir.

PPL, bu iki kategoride görev grupları içerir: yapılandırılmamış görev grupları ve yapılandırılmış görev grupları.PPL, yapılandırılmamış görev gruplarını temsil etmek için task_group sınıfını, yapılandırılmış görev gruplarını temsil etmek için ise structured_task_group sınıfını kullanır.

Önemli notÖnemli

PPL, paralel olarak bir görev kümesini yürütmek için structured_task_group sınıfını kullanan concurrency::parallel_invoke algoritmasını da tanımlar.parallel_invoke algoritması daha kısa sözdizimine sahip olduğundan mümkün olduğunda structured_task_group sınıfı yerine bunu kullanmanızı öneririz.Paralel Algoritmalar konusu parallel_invoke öğesini daha ayrıntılı olarak açıklar.

Aynı anda yürütmek istediğiniz birkaç bağımsız görev olduğunda ve devam etmeden önce tüm görevlerin bitmesin beklemeniz gerektiğinde parallel_invoke kullanın.Bu teknik, genellikle çatallanma ve birleşme paralelliği olarak adlandırılır.Aynı anda yürütmek istediğiniz birkaç bağımsız görev olduğunda ve daha sonra tüm görevlerin bitmesin beklemeniz gerektiğinde task_group kullanın.Örneğin görevleri bir task_group nesnesine ekleyebilir ve görevlerin başka bir işlevde ya da başka bir iş parçasından tamamlanmasını bekleyebilirsiniz.

Görev grupları iptali kavramını destekler.İptal, işlemin tamamını iptal etmek isteyen tüm etkin görevlere sinyal gönderilmesine olanak tanır.İptal, henüz baştan başlatılmayan görevleri de engeller.İptal işlemleri hakkında daha fazla bilgi için bkz. PPL'de İptal.

Çalışma zamanı ayrıca, bir görevden özel durum oluşturmanıza ve ilişkili görev grubunun tamamlanmasını beklerken o özel durumu işlemenize olanak veren bir özel durum işleme modeli de sağlar.Bu özel durum işleme modeli hakkında daha fazla bilgi için bkz. Eşzamanlılık Çalışma Zamanında Özel Durum İşleme.

[Üst]

task_group'u structured_task_group ile Karşılaştırma

structured_task_group sınıfları yerine task_group veya parallel_invoke kullanmanızı önermemize rağmen structured_task_group kullanmak isteyeceğiniz durumlar olabilir, örneğin değişken sayıda görevler gerçekleştiren veya iptal için destek gerektiren paralel algoritma yazarken.Bu bölümde, task_group ve structured_task_group sınıfları arasındaki farklar açıklanmaktadır.

task_group, iş parçacığı açısından güvenli bir sınıftır.Bu nedenle, bir task_group nesnesine birden fazla iş parçacığından görevler ekleyebilir ve birden fazla iş parçacığından bir task_group nesnesini bekleyebilir veya iptal edebilirsiniz.Bir structured_task_group nesnesini oluşturma ve silme işlemi aynı anlamsal bağlamda gerçekleşmelidir.Ayrıca, structured_task_group nesnesindeki tüm işlemlerin, aynı iş parçacığı üzerinde gerçekleşmesi gerekir.Bu kurala ait özel bir durum, concurrency::structured_task_group::cancel ve concurrency::structured_task_group::is_canceling yöntemleridir.Alt görev, herhangi bir zamanda üst görev grubunu iptal etmek ve iptali denetlemek için bu yöntemleri çağırabilir.

concurrency::task_group::wait veya concurrency::task_group::run_and_wait yöntemlerini çağırdıktan sonra task_group nesnesinde ek görevler çalıştırabilirsiniz.Buna karşılık, concurrency::structured_task_group::wait veya concurrency::structured_task_group::run_and_wait yöntemlerini çağırdıktan sonra structured_task_group nesnesinde ek görevler çalıştırıyorsanız, davranış tanımlanamaz.

structured_task_group sınıfı iş parçacıkları arasında eşitleme yapmadığından bu sınıfın yürütme yükü task_group sınıfına göre daha azdır.Bu nedenle, sorununuz birden çok iş parçacığından iş zamanlamanızı gerektirmiyorsa ve parallel_invoke algoritmasını kullanamıyorsanız, structured_task_group sınıfı daha iyi performans gösteren kodlar yazmanıza yardımcı olabilir.

Başka bir structured_task_group nesnesi içinde bir structured_task_group nesnesi kullanıyorsanız, iç nesne dış nesne tamamlanmadan önce tamamlanmalı ve yok edilmelidir.task_group sınıfı iç içe geçmiş görev gruplarının dıştaki grup tamamlanmadan bitmesini gerektirmez.

Yapılandırılmamış görev grupları ve görev tutamaçlı yapılandırılmış görev grupları farklı şekillerde çalışır.İş işlevlerini doğrudan task_group nesnesine geçirebilirsiniz; task_group nesnesi sizin için görev tanıtıcısını oluşturur ve yönetir.structured_task_group sınıfı her görev için bir task_handle nesnesi yönetmenizi gerektirir.Her task_handle nesnesi kendi ilgili structured_task_group nesnesinin kullanım süresi boyunca geçerli kalmalıdır.task_handle nesnesini oluşturmak için concurrency::make_task işlevini kullanın; bu işlem için aşağıdaki örneğe bakın:

// make-task-structure.cpp 
// compile with: /EHsc
#include <ppl.h>

using namespace concurrency;

int wmain()
{
   // Use the make_task function to define several tasks.
   auto task1 = make_task([] { /*TODO: Define the task body.*/ });
   auto task2 = make_task([] { /*TODO: Define the task body.*/ });
   auto task3 = make_task([] { /*TODO: Define the task body.*/ });

   // Create a structured task group and run the tasks concurrently.

   structured_task_group tasks;

   tasks.run(task1);
   tasks.run(task2);
   tasks.run_and_wait(task3);
}

Değişen sayıda göreviniz olan durumlara ilişkin görev tanıtıcılarını yönetmek için, _malloca gibi bir yığın ayırma yordamı veya std::vector gibi bir kapsayıcı sınıfı kullanın.

task_group ve structured_task_group iptal işlemini destekler.İptal işlemleri hakkında daha fazla bilgi için bkz. PPL'de İptal.

[Üst]

Örnek

Aşağıdaki basit örnekte, görev grupları ile nasıl çalışılacağı gösterilmiştir.Bu örnekte, iki görevi aynı anda gerçekleştirmek için parallel_invoke algoritması kullanılmaktadır.Her görev bir task_group nesnesine alt görevler ekler.task_group sınıfının birden çok görevin aynı anda görevler eklemesine olanak sağladığına dikkat edin.

// using-task-groups.cpp 
// compile with: /EHsc
#include <ppl.h>
#include <sstream>
#include <iostream>

using namespace concurrency;
using namespace std;

// Prints a message to the console. 
template<typename T>
void print_message(T t)
{
   wstringstream ss;
   ss << L"Message from task: " << t << endl;
   wcout << ss.str(); 
}

int wmain()
{  
   // A task_group object that can be used from multiple threads.
   task_group tasks;

   // Concurrently add several tasks to the task_group object.
   parallel_invoke(
      [&] {
         // Add a few tasks to the task_group object.
         tasks.run([] { print_message(L"Hello"); });
         tasks.run([] { print_message(42); });
      },
      [&] {
         // Add one additional task to the task_group object.
         tasks.run([] { print_message(3.14); });
      }
   );

   // Wait for all tasks to finish.
   tasks.wait();
}

Aşağıda, bu örnek için örnek çıktı gösterilmektedir:

  

parallel_invoke algoritması görevleri aynı anda çalıştırdığından çıktı iletilerinin sırası değişebilir.

parallel_invoke algoritmasının nasıl kullanıldığını gösteren komple örnekler için bkz. Nasıl yapılır: Paralel Sıralama Rutini Yazmak için parallel_invoke Kullanma ve Nasıl yapılır: Paralel İşlemleri Yürütmek için parallel_invoke Kullanma.Zaman uyumsuz ileri tarihli işleri uygulamak amacıyla task_group sınıfını kullanan komple bir örnek için bkz. İzlenecek yol: Vadeli İşlemleri Uygulama.

[Üst]

Güçlü Programlama

Görevler, görev grupları ve paralel algoritmalar kullandığınızda iptal rolünü ve özel durum işlemeyi anladığınızdan emin olun.Örneğin bir paralel iş ağacında iptal edilen bir görev alt görevlerin çalışmasını önleyebilir.Alt görevlerden biri, uygulamanız için önemli bir işlem yaparsa (örneğin, bir kaynağı serbest bırakmak) bu sorunlara neden olabilir.Ayrıca, bir alt görev özel bir durum görüntülerse, bu özel durum nesne yokedici yoluyla yayılabilir ve uygulamada tanımlanamayan bir davranışa yol açabilirdi.Bu noktaları gösteren bir örnek için bkz. İptal ve Özel Durum Yönetiminin Nesne İmhasını Nasıl Etkilediğini Anlama bölümü, En İyi Pratikler, Paralel Desenler Kitaplığı belgesi.PPL'de iptal ve özel durum işleme modelleri hakkında daha fazla bilgi için bkz. PPL'de İptal ve Eşzamanlılık Çalışma Zamanında Özel Durum İşleme.

[Üst]

İlgili Konular

Başlık

Tanımlama

Nasıl yapılır: Paralel Sıralama Rutini Yazmak için parallel_invoke Kullanma

Bitonic sıralama algoritmasının performansını artırmak için parallel_invoke algoritmasının nasıl kullanılacağını gösterir.

Nasıl yapılır: Paralel İşlemleri Yürütmek için parallel_invoke Kullanma

Bir paylaşılan veri kaynağı üzerinde birden çok işlemi gerçekleştiren bir programın performansını artırmak için parallel_invoke algoritmasının nasıl kullanılacağını gösterir.

Nasıl yapılır: Bir Gecikmeden Sonra Tamamlanan bir Görev Oluşturma

Bir gecikmeden sonra tamamlanan bir görev oluşturmak için task, cancellation_token_source, cancellation_token, ve task_completion_event sınıflarının nasıl kullanılacağını gösterir.

İzlenecek yol: Vadeli İşlemleri Uygulama

Daha fazlasını yapan bir şeyi Eşzamanlılık Çalışma zamanındaki varolan işlevsellikte nasıl birleştirileceğini gösterir.

Paralel Desen Kitaplığı (PPL)

Geliştirilen eş zamanlı uygulamalar için zorunlu programlama modeli sağlayan PPL'yi açıklar.

Başvuru

task Sınıfı (Eşzamanlılık Çalışma Zamanı)

task_completion_event Sınıfı

when_all İşlevi

when_any İşlevi

task_group Sınıfı

parallel_invoke İşlevi

structured_task_group Sınıfı