Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Tavsiye
Bu konuyu baştan okumanızı önersek de, C++/CX zaman uyumsuzunu C++/WinRT'ye taşımaya genel bakış bölümünde doğrudan birlikte çalışma tekniklerinin özetine geçebilirsiniz.
Bu, C++/CX'ten aşamalı olarak C++/WinRT'ye taşımayla ilgili gelişmiş bir konudur. Bu konu, C++/WinRT ile C++/CX birlikte çalışabilirliği konusunun kaldığı yerden devam eder.
Kod tabanınızın boyutu veya karmaşıklığı projenizi aşamalı olarak taşımayı gerekli kılıyorsa, bir süre için C++/CX ve C++/WinRT kodunun aynı projede yan yana bulunduğu bir taşıma işlemine ihtiyacınız olacaktır. Zaman uyumsuz kodunuz varsa, kaynak kodunuzu aşamalı olarak taşıma aşamasında projenizde paralel desen kitaplığı (PPL) görev zincirlerinin ve eş yordamların yan yana mevcut olması gerekebilir. Bu konu, zaman uyumsuz C++/CX kodu ile zaman uyumsuz C++/WinRT kodu arasında birlikte çalışma tekniklerine odaklanır. Bu teknikleri tek tek veya birlikte kullanabilirsiniz. Teknikler, her değişikliğin proje boyunca denetimsiz bir şekilde art arda eklenmesine gerek kalmadan, projenizin tamamını taşıma yolunda aşamalı, denetimli, yerel değişiklikler yapmanıza olanak sağlar.
Bu konuyu okumadan önce C++/WinRT ile C++/CX arasında birlikte çalışma konusunu okumak iyi bir fikirdir. Bu konu başlığında, projenizi aşamalı taşıma için nasıl hazırlayabileceğiniz gösterilmektedir. Ayrıca, bir C++/CX nesnesini C++/WinRT nesnesine dönüştürmek için kullanabileceğiniz iki yardımcı işlevi de tanıtır (ve tam tersi). Eşzamansızlıkla ilgili bu konu, bu bilgileri temel alır ve bu yardımcı işlevleri kullanır.
Note
C++/CX'ten C++/WinRT'ye aşamalı olarak taşımayla ilgili bazı sınırlamalar vardır. Windows Çalışma Zamanı bileşen projeniz varsa aşamalı olarak taşıma mümkün değildir ve projeyi tek geçişte taşımanız gerekir. Bir XAML projesi için, herhangi bir zamanda XAML sayfa türlerinizin tümü C ++/WinRT veya tüm C++/CX olmalıdır. Daha fazla bilgi için C++/CX'ten C++/WinRT'ye taşıma konusuna bakın.
Bir konunun tamamının zaman uyumsuz kod birlikte çalışma için ayrılmış olmasının nedeni
C++/CX'ten C++/WinRT'ye taşıma işlemi genellikle basittir; paralel desen kitaplığı (PPL) görevlerinden eş yordamlara geçiş dışında. Modeller farklıdır. PPL görevlerinden coroutine'lere doğal bire bir eşleme yoktur ve kodu mekanik olarak taşımanın basit bir yolu (her durumda işe yarar) yoktur.
İyi haber, görevlerden eş yordamlara dönüştürmenin önemli basitleştirmelerle sonuçlanıyor olmasıdır. Geliştirme ekipleri düzenli olarak zaman uyumsuz kodlarını taşıma engelini aştıklarında taşıma işinin geri kalanının büyük ölçüde mekanik olduğunu bildiriyor.
Genellikle, bir algoritma ilk olarak zaman uyumlu API'lere uyacak şekilde yazılmıştır. Ardından bu, görevlere ve açık devamlılıklara çevrildi; sonuç genellikle temel alınan mantığın yanlışlıkla gizlenmesi oldu. Örneğin döngüler özyineleme haline gelir; if-else dalları iç içe yerleştirilmiş bir görev ağacına (bir zincir) dönüşür; paylaşılan değişkenler shared_ptr olur. PPL kaynak kodunun çoğu zaman doğal olmayan yapısını çözümlemek için, önce bir adım geri çekilip orijinal kodun amacını anlamanızı (yani, orijinal eşzamanlı sürümü ortaya çıkarmanızı) öneririz. Ardından co_await uygun yerlere ekleyin (işbirliğiyle bekleyin).
Bu nedenle, bağlantı noktanızı başlatmak için zaman uyumsuz kodun C# (C++/CX yerine) sürümüne sahipseniz, bu size daha kolay bir zaman ve daha temiz bir bağlantı noktası sağlayabilir. C# kodu, await kullanır. Bu nedenle C# kodu zaten zaman uyumlu bir sürümle başlayıp uygun yerlere ekleme await felsefesini izler.
Projenizin C# sürümüne sahip değilseniz , bu konuda açıklanan teknikleri kullanabilirsiniz. C++/WinRT’ye taşıdıktan sonra, isterseniz eşzamansız kodunuzun yapısını C#’a taşımak da daha kolay olacaktır.
Asenkron programlama hakkında bazı temel bilgiler
Zaman uyumsuz programlama kavramları ve terminolojisi için ortak bir referans çerçevemiz olması amacıyla, önce genel olarak Windows Çalışma Zamanı’taki zaman uyumsuz programlamaya ilişkin kısaca bir çerçeve çizelim; ardından da iki C++ dil projeksiyonunun bunun üzerine kendi farklı biçimlerinde nasıl katmanlandığına bakalım.
Projenizde zaman uyumsuz olarak çalışan yöntemler vardır ve iki ana tür vardır.
- Başka bir işlem gerçekleştirmeden önce zaman uyumsuz çalışmanın tamamlanmasını beklemek yaygın bir durum. Zaman uyumsuz bir işlem nesnesi döndüren yöntem, bekleyebileceğiniz yöntemdir.
- Ancak bazen eşzamansız olarak yapılan işin tamamlanmasını beklemek istemezsiniz ya da buna gerek duymazsınız. Bu durumda, zaman uyumsuz yöntemin zaman uyumsuz bir işlem nesnesi döndürmemesi daha verimlidir. Böyle bir zaman uyumsuz yöntem — yani tamamlanmasını beklemediğiniz bir yöntem — fire-and-forget yöntemi olarak bilinir.
Windows Çalışma Zamanı zaman uyumsuz nesneleri (IAsyncXxx)
Windows::Foundation Windows Çalışma Zamanı ad alanı dört tür zaman uyumsuz işlem nesnesi içerir.
- IAsyncAction,
- IAsyncActionWithProgress<TProgress>,
- IAsyncOperation<TResult> ve
- IAsyncOperationWithProgress<TResult, TProgress>.
Bu konu başlığında , IAsyncXxx'in kullanışlı kısaltmasını kullandığımızda, bu türlerden toplu olarak söz ediyoruz; veya hangisini belirtmeye gerek kalmadan dört türden birinden bahsediyoruz.
C++/CX eşzamansız
Zaman uyumsuz C++/CX kodu Paralel Desen Kitaplığı (PPL) görevlerini kullanır. PPL görevi eşzamanlılık::görev sınıfı tarafından temsil edilir.
Genellikle zamanuyumsuz bir C++/CX metodu, PPL görevlerini lambda işlevlerini kullanarak concurrency::create_task ve concurrency::task::then ile birbirine zincirler. Her lambda işlevi, tamamlandığında bir değer üreten ve bu değerin daha sonra görevin devamı için kullanılan lambda işlevine geçirildiği bir görev döndürür.
Alternatif olarak, zaman uyumsuz bir C++/CX yöntemi, görev oluşturmak için create_task çağırmak yerine eşzamanlılık::create_async çağrısı yaparak bir IAsyncXxx^ oluşturabilir.
Dolayısıyla zaman uyumsuz bir C++/CX yönteminin dönüş türü bir PPL görevi veya IAsyncXxx^olabilir.
Her iki durumda da yöntemin kendisi, zaman uyumsuz bir nesne döndürmek için anahtar sözcüğünü kullanır return . Bu nesne tamamlandığında çağıranın gerçekten istediği değeri (belki bir dosya, bayt dizisi veya Boole değeri) üretir.
Note
Zaman uyumsuz bir C++/CX yöntemi bir IAsyncXxx^ döndürürse, TResult (varsa) Windows Çalışma Zamanı türüyle sınırlıdır. Örneğin, bir Boolean değeri bir Windows Çalışma Zamanı türüdür; ancak bir C++/CX yansıtılmış türü (örneğin, Platform::Array<byte>^) değildir.
C++/WinRT eşzamansız
C++/WinRT, C++ eş yordamlarını programlama modeliyle tümleştirir. Coroutines ve co_await deyimi, birlikte bir sonuç beklemek için doğal bir yol sağlar.
IAsyncXxx türlerinin her biri winrt::Windows::Foundation C++/WinRT ad alanında karşılık gelen bir türe yansıtılır. Şimdi bunları winrt::IAsyncXxx olarak adlandıralım (C++/CX'in IAsyncXxx^ değeriyle karşılaştırıldığında).
Bir C++/WinRT eşyordamının dönüş türü ya winrt::IAsyncXxx ya da winrt::fire_and_forget'dir. Ve zaman uyumsuz bir nesneyi döndürmek için return anahtar sözcüğünü kullanmak yerine, bir eş yordam, çağıranın gerçekten istediği değeri (örneğin bir dosya, bir bayt dizisi veya bir Boole değeri) işbirliği içinde geri döndürmek için co_return anahtar sözcüğünü kullanır.
Bir yöntem en az bir co_await ifadesi (veya en az bir co_return ya da co_yield) içeriyorsa, bu yöntem bu nedenle bir eş yordamdır.
Daha fazla bilgi ve kod örnekleri için bkz. C++/WinRT ile eşzamanlılık ve zaman uyumsuz işlemler.
Direct3D oyun örneği (Simple3DGameDX)
Bu konu, zaman uyumsuz kodu aşamalı olarak taşımayı gösteren birkaç belirli programlama tekniğinin kılavuzlarını içerir. Örnek olay incelemesi olarak hizmet vermek için Direct3D oyun örneğinin (Simple3DGameDX olarak adlandırılır) C++/ CX sürümünü kullanacağız. Bu projede özgün C++/CX kaynak kodunu nasıl alabileceğinize ve zaman uyumsuz kodunu aşamalı olarak C++/WinRT'ye nasıl aktarabileceğinize ilişkin bazı örnekler göstereceğiz.
- Yukarıdaki bağlantıdan ZIP'i indirin ve sıkıştırmasını açın.
- C++/CX projesini (adlı
cppklasördedir) Visual Studio açın. - Ardından projeye C++/WinRT desteği eklemeniz gerekir. Bunu yapmak için izlediğiniz adımlar C++/CX projesi alma ve C++/WinRT desteği ekleme bölümünde açıklanmıştır. Bu bölümde, üst bilgi dosyasını projenize ekleme
interop_helpers.hadımı özellikle önemlidir çünkü bu konudaki yardımcı işlevlere bağlı olacağız. - Son olarak,
#include <pplawait.h>öğesinipch.höğesine ekleyin. Bu size PPL için eş yordam desteği sağlar (aşağıdaki bölümde bu destek hakkında daha fazla bilgi vardır).
Henüz oluşturmayın, aksi takdirde bayt belirsizliğiyle ilgili hatalar alırsınız. Bu sorunu şu şekilde çözebilirsiniz.
-
BasicLoader.cppöğesini açın veusing namespace std;öğesini yorum satırı hâline getirin. - Aynı kaynak kodu dosyasında, shared_ptrstd::shared_ptr olarak nitelemeniz gerekir. Bunu, bu dosyanın içinde bir arama ve değiştirme işlemiyle yapabilirsiniz.
- Ardından vector'ü std::vector ve string'i ise std::string olarak niteleyin.
Proje artık yeniden derlenebiliyor, C++/WinRT desteği vardır ve from_cx ile to_cx birlikte çalışma yardımcı işlevlerini içerir.
Artık bu bölümdeki kodun adım adım anlatımını takip etmek için Simple3DGameDX projeniz hazır.
C++/CX async yapısını C++/WinRT'ye taşımaya genel bakış
Özetle, taşıma işlemi sırasında PPL görev zincirlerini co_await çağrılarına dönüştüreceğiz. PpL görevinden bir yöntemin dönüş değerini C++/WinRT winrt::IAsyncXxx nesnesine değiştireceğiz. Ayrıca herhangi bir IAsyncXxx^ öğesini C++/WinRT winrt::IAsyncXxxx olarak değiştireceğiz.
Coroutine öğesini çağıran co_xxxherhangi bir yöntem olduğunu hatırlayacaksınız. Bir C++/WinRT coroutine’i, değerini işbirliği içinde döndürmek için co_return kullanır. PPL için pplawait.h sayesinde sunulan coroutine desteğiyle, bir coroutine’den PPL görevi döndürmek için co_return öğesini de kullanabilirsiniz. Ayrıca co_await hem her iki görevi de hem de IAsyncXxx'i kullanabilirsiniz. Ancak, bir IAsyncXxx^ döndürmek için co_return kullanamazsınız. Aşağıdaki tabloda, resimdeki çeşitli pplawait.h zaman uyumsuz teknikler arasında birlikte çalışma desteği açıklanmaktadır.
| Method |
co_await bunu yapabilir misin? |
Bunu yapabilir misin co_return ? |
|---|---|---|
| Yöntem , görevi<geçersiz kılar> | Yes | Yes |
| Yöntem task<T> döndürür | No | Yes |
| Yöntem IAsyncXxx döndürür^ | Yes | Hayır. Ama co_return kullanan bir görevi create_async ile sararsınız. |
| Yöntem winrt::IAsyncXxx döndürür | Yes | Yes |
Bu sonraki tabloyu kullanarak doğrudan bu konunun ilgilendiğiniz birlikte çalışma tekniğini açıklayan bölüme atlayın veya buradan okumaya devam edin.
| Asenkron birlikte çalışabilirlik tekniği | Bu konudaki bölüm |
|---|---|
Bir fire-and-forget yöntemi içinden veya bir oluşturucu içinde, task<void> yöntemini beklemek için co_await kullanın. |
Fire-and-forget yönteminde görev<boş bekleme> |
Bir task<void> yönteminin içinden bir task<void> yöntemini beklemek için co_await kullanın. |
task<void> yöntemi içinde task<void> bekleyin |
Bir task<T> yöntemi içinden bir task<void> yöntemini beklemek için co_await kullanın. |
Bir task<T> yöntemi içinde task<void> bekleyin |
co_await
IAsyncXxx^ yöntemini beklemek için kullanın. |
Bir task yönteminde IAsyncXxx^ bekleyin, projenin geri kalanını değiştirmeden |
co_return öğesini bir task<void> yöntemi içinde kullanın. |
task<void> ifadesini bir task<void> yöntemi içinde bekleme |
task<T> yöntemi içinde co_return kullanın. |
Bir görev yönteminde IAsyncXxx^ beklenir ve projenin geri kalanı değişmeden bırakılır |
co_return kullanan bir görevi create_async ile sarmalayın. |
create_async kullanan bir görevin çevresinde kaydırmaco_return |
| Bağlantı noktası eşzamanlılığı::wait. |
concurrency::wait öğesini co_await winrt::resume_after öğesine taşı |
| task<void> yerine winrt::IAsyncXxx döndürün. | task< void> dönüş türünü winrt::IAsyncXxx'e taşıyın |
| Bir winrt::IAsyncXxx<T>'yi (T ilkel bir türdür) bir task<T>'ye dönüştürün. | Winrt::IAsyncXxx<T> (T ilkeldir) görevini T< görevine> dönüştürme |
| Bir winrt::IAsyncXxx<T>'yi (T, bir Windows Çalışma Zamanı türüdür) task<T^>'ye dönüştürün. | Winrt::IAsyncXxx<T> (T Windows Çalışma Zamanı türüdür) görevini T^< görevine> dönüştürme |
Desteklerden bazılarını gösteren kısa bir kod örneği aşağıda verilmiş.
#include <ppltasks.h>
#include <pplawait.h>
#include <winrt/Windows.Foundation.h>
concurrency::task<bool> TaskAsync()
{
co_return true;
}
Windows::Foundation::IAsyncOperation<bool>^ IAsyncXxxCppCXAsync()
{
// co_return true; // Error! Can't do that. But you can do
// the following.
return concurrency::create_async([=]() -> concurrency::task<bool> {
co_return true;
});
}
winrt::Windows::Foundation::IAsyncOperation<bool> IAsyncXxxCppWinRTAsync()
{
co_return true;
}
concurrency::task<bool> CppCXAsync()
{
bool b1 = co_await TaskAsync();
bool b2 = co_await IAsyncXxxCppCXAsync();
co_return co_await IAsyncXxxCppWinRTAsync();
}
winrt::fire_and_forget CppWinRTAsync()
{
bool b1 = co_await TaskAsync();
bool b2 = co_await IAsyncXxxCppCXAsync();
bool b3 = co_await IAsyncXxxCppWinRTAsync();
}
Önemli
Bu harika birlikte çalışabilirlik seçeneklerine rağmen, aşamalı geçiş yapabilmek, projenin geri kalanını etkilemeden nokta atışı yapabileceğimiz değişiklikleri seçmemize bağlıdır. Rastgele bir gevşek uçta sallanmaktan ve böylece tüm projenin yapısının çözülmesinden kaçınmak istiyoruz. Bunun için, işleri belirli bir sırayla yapmamız gerekir. Şimdi bu tür asenkronla ilgili taşıma ve birlikte çalışabilirlik değişikliklerine ilişkin bazı örnekleri yakından inceleyeceğiz.
Projenin geri kalanını değiştirmeden bir görev<geçersiz kılma> yöntemi bekle
Void< görevini> döndüren bir yöntem çalışmayı zaman uyumsuz olarak gerçekleştirir ve zaman uyumsuz bir işlem nesnesi döndürür, ancak sonuçta bir değer üretmez. Böyle bir yöntem kullanabiliriz co_await .
Bu nedenle zaman uyumsuz kodu aşamalı olarak taşımaya başlamak için iyi bir yer, bu tür yöntemleri çağırdığınız yerleri bulmaktır. Bu noktalarda bir görev oluşturma ve/veya döndürme söz konusudur. Ayrıca, her görevden onun devamına hiçbir değerin aktarılmadığı türden görev zincirlerini de içerebilirler. Bu gibi yerlerde, göreceğimiz gibi, asenkron kodu co_await ifadeleriyle değiştirebilirsiniz.
Note
Bu konu ilerledikçe bu stratejinin avantajını göreceksiniz. Belirli bir task<void> yöntemi yalnızca co_await aracılığıyla çağrılmaya başlandıktan sonra, bu yöntemi C++/WinRT'ye taşıyabilir ve winrt::IAsyncXxx döndürecek şekilde düzenleyebilirsiniz.
Şimdi bazı örnekler bulalım. Simple3DGameDX projesini açın (bkz. Direct3D oyun örneği).
Önemli
Aşağıdaki örneklerde, yöntemlerin uygulamalarının değiştirildiğini gördüğünüzde, değiştirdiğimiz yöntemlerin çağıranlarını değiştirmemiz gerekmeyen bir durum olduğunu unutmayın. Bu değişiklikler yerelleştirilir ve proje boyunca art arda gelmez.
Fire-and-forget yönteminde task<void> bekleyin
En basit durum bu olduğu için, fire-and-forget yöntemlerinde task<void> beklemekle başlayalım. Bunlar zaman uyumsuz olarak çalışan yöntemlerdir, ancak yöntemini çağıran bu çalışmanın tamamlanmasını beklemez. Yöntemi yalnızca çağırır ve eşzamansız olarak tamamlanmasına rağmen onu unutursunuz.
Projenizin bağımlılık grafiğinin kökünde, create_task içeren void yöntemleri ve/veya yalnızca task<void> yöntemlerinin çağrıldığı görev zincirlerini arayın.
Simple3DGameDX'te, GameMain::Update yönteminin uygulanmasında bunun gibi bir kod bulacaksınız. Kaynak kod dosyasında GameMain.cppbulunur.
GameMain::Update
Burada, yöntemin C++/CX sürümünden, zaman uyumsuz olarak tamamlanan iki bölümünü gösteren bir alıntı yer alıyor.
void GameMain::Update()
{
...
case UpdateEngineState::WaitingForPress:
...
m_game->LoadLevelAsync().then([this]()
{
m_game->FinalizeLoadLevel();
m_updateState = UpdateEngineState::ResourcesLoaded;
}, task_continuation_context::use_current());
...
case UpdateEngineState::Dynamics:
...
m_game->LoadLevelAsync().then([this]()
{
m_game->FinalizeLoadLevel();
m_updateState = UpdateEngineState::ResourcesLoaded;
}, task_continuation_context::use_current());
...
...
}
Simple3DGame::LoadLevelAsync yöntemine (PPL görevi<geçersiz> değerini döndüren) bir çağrı görebilirsiniz. Ardından bazı eşzamanlı işlemler yapan bir devam gelir. LoadLevelAsync zaman uyumsuzdur, ancak bir değer döndürmez. Dolayısıyla, görevden devam işlevine herhangi bir değer aktarılmıyor.
Bu iki yerde kodda da aynı türde bir değişiklik yapabilirsiniz. Kod aşağıdaki listeden sonra açıklanmıştır. Burada, sınıf üyesi bir eş yordamda this işaretçisine güvenli bir şekilde nasıl erişileceğini tartışabiliriz. Ancak bunu ilerideki bir bölüme erteleyelim (co_await ve this işaretçisi hakkındaki ertelenmiş tartışma)—şimdilik bu kod çalışır.
winrt::fire_and_forget GameMain::Update()
{
...
case UpdateEngineState::WaitingForPress:
...
co_await m_game->LoadLevelAsync();
m_game->FinalizeLoadLevel();
m_updateState = UpdateEngineState::ResourcesLoaded;
...
case UpdateEngineState::Dynamics:
...
co_await m_game->LoadLevelAsync();
m_game->FinalizeLoadLevel();
m_updateState = UpdateEngineState::ResourcesLoaded;
...
...
}
Gördüğünüz gibi LoadLevelAsync bir görev döndürdüğü için bunu yapabilirsiniz co_await . Ayrıca açık bir devamlılık gerekmez; izleyen kod co_await yalnızca LoadLevelAsync tamamlandığında yürütülür.
co_await eklenmesi yöntemi bir eş yordama dönüştürür, bu nedenle dönüş değeri olarak void bırakamadık. Bu bir fire-and-forget yöntemi olduğundan winrt::fire_and_forget döndürecek şekilde değiştirdik.
GameMain.h öğesini de düzenlemeniz gerekecek. Oradaki bildirim kısmında da GameMain::Update öğesinin dönüş türünü void değerinden winrt::fire_and_forget olarak değiştirin.
Proje kopyanızda bu değişikliği yapabilirsiniz ve oyun hala aynı şekilde derlenip çalıştırılır. Kaynak kod hala temel olarak C++/CX'tir, ancak artık C++/WinRT ile aynı desenleri kullanıyor, bu nedenle kodun geri kalanını mekanik olarak taşımaya biraz daha yaklaştık.
GameMain::ResetGame
GameMain::ResetGame başka bir fire-and-forget yöntemidir; LoadLevelAsync'i de çağırır. Böylece, pratik yapmak istiyorsanız aynı kod değişikliğini orada da yapabilirsiniz.
GameMain::OnDeviceRestored
Bir no-op görevi de dahil olmak üzere zaman uyumsuz kodun daha derin iç içe yerleştirilmiş olması nedeniyle GameMain::OnDeviceRestored uygulamasında işler biraz daha ilginç hale geldi. İşte yöntemin eşzamansız kısımlarının genel hatları (daha az ilgi çekici senkron kod bölümleri üç noktayla gösterilmiştir).
void GameMain::OnDeviceRestored()
{
...
create_task([this]()
{
return m_renderer->CreateGameDeviceResourcesAsync(m_game);
}).then([this]()
{
...
if (m_updateState == UpdateEngineState::WaitingForResources)
{
...
return m_game->LoadLevelAsync().then([this]()
{
...
}, task_continuation_context::use_current());
}
else
{
return create_task([]()
{
// Return a no-op task.
});
}
}, task_continuation_context::use_current()).then([this]()
{
...
}, task_continuation_context::use_current());
}
İlk olarak, GameMain.h ve .cpp içinde GameMain::OnDeviceRestored öğesinin dönüş türünü void yerine winrt::fire_and_forget olarak değiştirin. Ayrıca DeviceResources.h öğesini açmanız ve IDeviceNotify::OnDeviceRestored yönteminin dönüş türünde aynı değişikliği yapmanız gerekir.
Eşzamansız kodu uyarlamak için tüm create_task ve then çağrılarını ve bunların süslü parantezlerini kaldırın; yöntemi de düz bir ifade dizisine sadeleştirin.
Task döndüren tüm return öğelerini co_await olarak değiştirin. Geriye hiçbir şey döndürmeyen bir return kalacak, bu nedenle onu silmeniz yeter. İşiniz bittiğinde, no-op görevi ortadan kaybolmuş olacak ve yöntemin asenkron bölümlerinin genel görünümü şöyle olacak. Yine, daha az ilginç olan eşzamanlı kod atlanmıştır.
winrt::fire_and_forget GameMain::OnDeviceRestored()
{
...
co_await m_renderer->CreateGameDeviceResourcesAsync(m_game);
...
if (m_updateState == UpdateEngineState::WaitingForResources)
{
co_await m_game->LoadLevelAsync();
...
}
...
}
Gördüğünüz gibi, bu eşzamansız yapı çok daha basittir ve okunması daha kolaydır.
GameMain::GameMain
GameMain::GameMain oluşturucu, çalışmayı zaman uyumsuz olarak gerçekleştirir ve projenin hiçbir parçası bu çalışmanın tamamlanmasını beklemez. Yine, bu liste eşzamansız bölümleri özetler.
GameMain::GameMain(...) : ...
{
...
create_task([this]()
{
...
return m_renderer->CreateGameDeviceResourcesAsync(m_game);
}).then([this]()
{
...
if (m_updateState == UpdateEngineState::WaitingForResources)
{
return m_game->LoadLevelAsync().then([this]()
{
...
}, task_continuation_context::use_current());
}
else
{
return create_task([]()
{
// Return a no-op task.
});
}
}, task_continuation_context::use_current()).then([this]()
{
....
}, task_continuation_context::use_current());
}
Ancak bir oluşturucu winrt::fire_and_forget döndüremez; bu nedenle eşzamansız kodu yeni bir GameMain::ConstructInBackground başlat ve unut yöntemine taşıyacak, kodu co_await deyimlerine dönüştürecek ve yeni yöntemi oluşturucudan çağıracağız. Sonuç şu şekildedir.
GameMain::GameMain(...) : ...
{
...
ConstructInBackground();
}
winrt::fire_and_forget GameMain::ConstructInBackground()
{
...
co_await m_renderer->CreateGameDeviceResourcesAsync(m_game);
...
if (m_updateState == UpdateEngineState::WaitingForResources)
{
...
co_await m_game->LoadLevelAsync();
...
}
...
}
Artık GameMain'deki tüm fire-and-forget yöntemler —hatta tüm asenkron kodlar— korutinlere dönüştürüldü. İsterseniz, diğer sınıflarda fire-and-forget yöntemlerini arayıp benzer değişiklikler yapabilirsiniz.
co_await ve this işaretçisi hakkında ertelenen tartışma
GameMain::Update'de değişiklik yaparken, bu işaretçiyle ilgili bir tartışmayı erteledim. Bu tartışmayı burada yapalım.
Bu durum, şimdiye kadar değiştirdiğimiz tüm metotlar için geçerlidir; ve yalnızca çalıştır ve unut türündekiler için değil, tüm eş yordamlar için de geçerlidir. Bir yönteme co_await eklemek, askıya alma noktası oluşturur. Bu nedenle, bir sınıf üyesine her erişişimizde askıya alma noktasından sonra kullandığımız bu işaretçiye dikkat etmeliyiz.
Kısa hikaye, çözümün implements::get_strong çağrısı yapmak olmasıdır. Ancak, sorunun ve çözümün ayrıntılı açıklaması için bkz. Bir sınıf üyesi eş yordamında this işaretçisine güvenli erişim.
yalnızca winrt::implements öğesinden türetilen bir sınıfta implements::get_strong çağırabilirsiniz.
Winrt::implements'tenGameMain türet
Yapmamız gereken ilk değişiklik GameMain.h içindedir.
class GameMain :
public DX::IDeviceNotify
GameMainDX::IDeviceNotify'ı uygulamaya devam edecek, ancak winrt::implements'ten türetilecek şekilde değiştireceğiz.
class GameMain :
public winrt::implements<GameMain, winrt::Windows::Foundation::IInspectable>,
DX::IDeviceNotify
Ardından, içinde App.cppbu yöntemi bulacaksınız.
void App::Load(Platform::String^)
{
if (!m_main)
{
m_main = std::unique_ptr<GameMain>(new GameMain(m_deviceResources));
}
}
Ama artık GameMain, winrt::implements'den türediğine göre, onu farklı bir şekilde oluşturmamız gerekiyor. Bu durumda winrt::make_self işlev şablonunu kullanacağız. Daha fazla bilgi için bkz . Uygulama türlerini ve arabirimlerini örnekleme ve döndürme.
Bu kod satırını bununla değiştirin.
...
m_main = winrt::make_self<GameMain>(m_deviceResources);
...
Bu değişiklikle ilgili döngünün kapatılması için m_main türünü de değiştirmemiz gerekir. içinde App.hbu kodu bulacaksınız.
ref class App sealed :
public Windows::ApplicationModel::Core::IFrameworkView
{
...
private:
...
std::unique_ptr<GameMain> m_main;
};
m_main bildirimini bununla değiştirin.
...
winrt::com_ptr<GameMain> m_main;
...
Artık implements::get_strong çağırabiliriz
GameMain::Update için ve co_await eklediğimiz diğer yöntemlerden herhangi biri için, güçlü başvurunun coroutine tamamlanana kadar korunmasını sağlamak üzere, bir eş yordamın başında get_strong işlevini şu şekilde çağırabilirsiniz.
winrt::fire_and_forget GameMain::Update()
{
auto strong_this{ get_strong() }; // Keep *this* alive.
...
co_await ...
...
}
task<void> yöntemi içinde task<void>'u bekleyin
Bir sonraki en basit durum, kendisi de task<void> döndüren bir yöntem içinde task<void> beklemektir. Çünkü bir co_awaitgörevi< geçersiz> kılabiliriz ve birinden co_return yapabiliriz.
Simple3DGame::LoadLevelAsync yönteminin uygulanmasında çok basit bir örnek bulacaksınız. Kaynak kod dosyasında Simple3DGame.cppbulunur.
task<void> Simple3DGame::LoadLevelAsync()
{
m_level[m_currentLevel]->Initialize(m_objects);
m_levelDuration = m_level[m_currentLevel]->TimeLimit() + m_levelBonusTime;
return m_renderer->LoadLevelResourcesAsync();
}
Yalnızca biraz eşzamanlı kod bulunur; ardından da GameRenderer::LoadLevelResourcesAsync'in oluşturduğu görev döndürülür.
Bu görevi döndürmek yerine, bunu ve ardından co_await sonuçta elde edilen co_returnöğesini döndürürizvoid.
task<void> Simple3DGame::LoadLevelAsync()
{
m_level[m_currentLevel]->Initialize(m_objects);
m_levelDuration = m_level[m_currentLevel]->TimeLimit() + m_levelBonusTime;
co_return co_await m_renderer->LoadLevelResourcesAsync();
}
Bu derin bir değişikliğe benzemiyor. Ancak artık co_await üzerinden GameRenderer::LoadLevelResourcesAsync çağırdığımıza göre, bunu bir task yerine winrt::IAsyncXxx döndürecek şekilde taşıyabiliriz. Bunu daha sonra task<void> dönüş türünü winrt::IAsyncXxx'e taşıma bölümünde yapacağız.
task<T> yönteminde task<void> bekleyin
Simple3DGameDX'te bulunabilecek uygun örnekler olmasa da, yalnızca deseni göstermek için varsayımsal bir örnek oluşturabiliriz.
Aşağıdaki kod örneğindeki ilk satır, bir task<void>co_await yapısının basit kullanımını göstermektedir. Ardından, task<T> dönüş türünü sağlamak için StorageFile^ nesnesini zaman uyumsuz biçimde döndürmemiz gerekir. Bunu yapmak için bir Windows Çalışma Zamanı API'sini ve co_await sonuçta elde edilen dosyayı kullanırızco_return.
task<StorageFile^> Simple3DGame::LoadLevelAndRetrieveFileAsync(
StorageFolder^ location,
Platform::String^ filename)
{
co_await m_renderer->LoadLevelResourcesAsync();
co_return co_await location->GetFileAsync(filename);
}
Bunun gibi yöntemin daha fazlasını C++/WinRT'ye de taşınabilirdik.
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::StorageFile>
Simple3DGame::LoadLevelAndRetrieveFileAsync(
StorageFolder location,
std::wstring filename)
{
co_await m_renderer->LoadLevelResourcesAsync();
co_return co_await location.GetFileAsync(filename);
}
m_renderer veri üyesi yine de bu örnekte C++/CX'tir.
Bir görev yönteminde IAsyncXxx^ beklenir ve projenin geri kalanı değişmeden bırakılır
Görevi nasıl > gördük. Ayrıca, projenizdeki bir yöntem ya da asenkron bir Windows API’sindeki bir yöntem olsun, co_awaitIAsyncXxx döndüren bir yöntemi de kullanabilirsiniz (örneğin, önceki bölümde await ettiğimiz StorageFolder.GetFileAsync).
Bu tür bir kod değişikliği gerçekleştirebileceğimiz bir örnek için BasicReaderWriter::ReadDataAsync'e göz atalım (içinde uygulandığını BasicReaderWriter.cppgöreceksiniz).
Özgün C++/CX sürümü aşağıdadır.
task<Platform::Array<byte>^> BasicReaderWriter::ReadDataAsync(
_In_ Platform::String^ filename
)
{
return task<StorageFile^>(m_location->GetFileAsync(filename)).then([=](StorageFile^ file)
{
return FileIO::ReadBufferAsync(file);
}).then([=](IBuffer^ buffer)
{
auto fileData = ref new Platform::Array<byte>(buffer->Length);
DataReader::FromBuffer(buffer)->ReadBytes(fileData);
return fileData;
});
}
Aşağıdaki kod listesi, co_awaitIAsyncXxx^ döndüren Windows API'lerini çağırabildiğimizi göstermektedir. Yalnızca bu değil, co_return zaman uyumsuz olarak döndürdüğü değeri de (bu örnekte bir bayt dizisi) de kullanabiliriz. Bu ilk adım yalnızca bu değişikliklerin nasıl yapılacağını gösterir; sonraki bölümde C++/CX kodunu C++/WinRT'ye taşımayı öğreneceksiniz.
task<Platform::Array<byte>^> BasicReaderWriter::ReadDataAsync(
_In_ Platform::String^ filename
)
{
StorageFile^ file = co_await m_location->GetFileAsync(filename);
IBuffer^ buffer = co_await FileIO::ReadBufferAsync(file);
auto fileData = ref new Platform::Array<byte>(buffer->Length);
DataReader::FromBuffer(buffer)->ReadBytes(fileData);
co_return fileData;
}
Dönüş türünü değiştirmediğimiz için değiştirdiğimiz yöntemlerin çağıranlarını değiştirmemiz gerekmez.
ReadDataAsync bağlantı noktasını (çoğunlukla) C++/WinRT'ye taşıma ve projenin geri kalanını değiştirmeden bırakma
Bir adım ileri gidebilir ve projenin başka bir bölümünü değiştirmeye gerek kalmadan yöntemi neredeyse tamamen C++/WinRT'ye taşıyabiliriz.
Bu yöntemin projenin geri kalanında sahip olduğu tek bağımlılık, C++/CX StorageFolder^ olan BasicReaderWriter::m_location veri üyesidir. Bu veri üyesini değiştirmeden bırakmak ve parametre türünü ve dönüş türünü değiştirmeden bırakmak için, biri yöntemin başında, diğeri de sonunda olmak üzere yalnızca birkaç dönüştürme gerçekleştirmemiz gerekir. Bunun için from_cx ve to_cx birlikte çalışabilirlik yardımcı işlevlerini kullanabiliriz.
BasicReaderWriter::ReadDataAsync uygulaması ağırlıklı olarak C++/WinRT'ye geçirildikten sonra şu şekilde görünür. Bu, kademeli geçişin iyi bir örneğidir. Ve bu yöntem, bazı C++/WinRT teknikleri kullanan bir C++/CX yöntemi olarak düşünmekten uzaklaşabildiğimiz ve bunu C++ /CX ile birlikte çalışan bir C++/WinRT yöntemi olarak görebileceğimiz aşamadadır.
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Streams.h>
#include <robuffer.h>
...
task<Platform::Array<byte>^> BasicReaderWriter::ReadDataAsync(
_In_ Platform::String^ filename)
{
auto location_from_cx = from_cx<winrt::Windows::Storage::StorageFolder>(m_location);
auto file = co_await location_from_cx.GetFileAsync(filename->Data());
auto buffer = co_await winrt::Windows::Storage::FileIO::ReadBufferAsync(file);
byte* bytes;
auto byteAccess = buffer.as<Windows::Storage::Streams::IBufferByteAccess>();
winrt::check_hresult(byteAccess->Buffer(&bytes));
co_return ref new Platform::Array<byte>(bytes, buffer.Length());
}
Note
Yukarıdaki ReadDataAsync'te yeni bir C++/CX dizisi oluşturup döndüreceğiz. Ve elbette bunu yöntemin dönüş türünü karşılamak için yaparız (böylece projenin geri kalanını değiştirmemiz gerekmez).
Kendi projenizde, taşımadan sonra yöntemin sonuna ulaştığınız ve sahip olduğunuz tek şeyin bir C++/WinRT nesnesi olduğu başka örneklerle karşılaşabilirsiniz.
co_returnBunu yapmak için, dönüştürmek üzere to_cx’i çağırmanız yeterlidir. Bu konuda daha fazla bilgi ve sonraki bölüme bir örnek verilmiştir.
winrt::IAsyncXxx<T>'yi task<T>'ye dönüştürme
Bu bölüm, zaman uyumsuz bir yöntemi C++/WinRT'ye ( winrt::IAsyncXxxx<T> döndürecek şekilde) taşıma durumunuzla ilgilidir, ancak hala bir görev döndürüyor gibi C++/CX kodunuz bu yöntemi çağırır.
- Bir durum, T'nin ilkel bir tür olmasıdır; bu da dönüştürme gerektirmez.
- Diğer durumda, T bir Windows Çalışma Zamanı türüdür ve bu durumda bunu T^ türüne dönüştürmeniz gerekir.
Bir winrt::IAsyncXxx<T>'yi (T ilkel türdür) task<T>'ye dönüştürme
Bu bölümdeki desen, ilkel bir değeri zaman uyumsuz olarak döndürdğünüzde geçerlidir (göstermek için Boole değeri kullanacağız). C++/WinRT'ye zaten aktardığınız bir yöntemin bu imzaya sahip olduğu bir örneği düşünün.
winrt::Windows::Foundation::IAsyncOperation<bool>
MyClass::GetBoolMemberFunctionAsync()
{
bool value = ...
co_return value;
}
Bu yönteme yapılan çağrıyı bunun gibi bir göreve dönüştürebilirsiniz.
task<bool> MyClass::RetrieveBoolTask()
{
co_return co_await GetBoolMemberFunctionAsync();
}
Ya da şöyle.
task<bool> MyClass::RetrieveBoolTask()
{
return concurrency::create_task(
[this]() -> concurrency::task<bool> {
auto result = co_await GetBoolMemberFunctionAsync();
co_return result;
});
}
Derleyici bunu çıkaramadığından lambda işlevinin görev dönüş türünün açık olduğuna dikkat edin.
Yöntemini bunun gibi rastgele bir görev zinciri içinden de çağırabiliriz. Yine açık bir lambda dönüş türüyle.
...
.then([this]() -> concurrency::task<bool> {
co_return co_await GetBoolMemberFunctionAsync();
}).then([this](bool result) {
...
});
...
Bir winrt::IAsyncXxx<T> öğesini (T, bir Windows Çalışma Zamanı türüdür) task<T^> öğesine dönüştürün
Bu bölümdeki örüntü, bir Windows Çalışma Zamanı değerini zaman uyumsuz biçimde döndürdüğünüzde uygulanır (örneklemek için StorageFile değerini kullanacağız). C++/WinRT'ye zaten aktardığınız bir yöntemin bu imzaya sahip olduğu bir örneği düşünün.
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::StorageFile>
MyClass::GetStorageFileMemberFunctionAsync()
{
co_return co_await winrt::Windows::Storage::StorageFile::GetFileFromPathAsync
(L"MyFile.txt");
}
Sonraki listede bu yönteme yapılan çağrının göreve nasıl dönüştürüldüğü gösterilir. Döndürülen C++/WinRT nesnesini bir C++/CX tanıtıcısına (diğer adıyla hat) dönüştürmek için to_cx birlikte çalışabilirlik yardımcı işlevini çağırmamız gerektiğine dikkat edin.
task<Windows::Storage::StorageFile^> RetrieveStorageFileTask()
{
winrt::Windows::Storage::StorageFile storageFile =
co_await GetStorageFileMemberFunctionAsync();
co_return to_cx<Windows::Storage::StorageFile>(storageFile);
}
İşte bunun daha kısa bir sürümü.
task<Windows::Storage::StorageFile^> RetrieveStorageFileTask()
{
co_return to_cx<Windows::Storage::StorageFile>(GetStorageFileMemberFunctionAsync());
}
Hatta bu deseni yeniden kullanılabilir bir işlev şablonuna sarmalamayı seçebilir ve return onu normalde bir görevi döndürdüğünüz gibi döndürebilirsiniz.
template<typename ResultTypeCX, typename Awaitable>
concurrency::task<ResultTypeCX^> to_task(Awaitable awaitable)
{
co_return to_cx<ResultTypeCX>(co_await awaitable);
}
task<Windows::Storage::StorageFile^> RetrieveStorageFileTask()
{
return to_task<Windows::Storage::StorageFile>(GetStorageFileMemberFunctionAsync());
}
Bu fikri beğendiyseniz, öğesine to_taskinterop_helpers.heklemek isteyebilirsiniz.
co_return kullanan bir görevi create_async ile sarmalayın
co_return^ yapamazsınız, ancak benzer bir şey elde edebilirsiniz. Birlikte bir değer döndüren bir göreviniz varsa, bunu eşzamanlılık::create_async çağrısının içine sarmalayabilirsiniz.
Aşağıda, Simple3DGameDX'ten kaldırabileceğiniz bir örnek olmadığından, varsayımsal bir örnek verilmiştir.
Windows::Foundation::IAsyncOperation<bool>^ MyClass::RetrieveBoolAsync()
{
return concurrency::create_async(
[this]() -> concurrency::task<bool> {
bool result = co_await GetBoolMemberFunctionAsync();
co_return result;
});
}
Gördüğünüz gibi, co_await yapabildiğiniz herhangi bir yöntemden dönüş değerini alabilirsiniz.
concurrency::wait öğesini taşı co_await winrt::resume_after
Simple3DGameDX'in, iş parçacığını kısa bir süre duraklatmak için concurrency::wait kullandığı birkaç yer vardır. İşte bir örnek.
// GameConstants.h
namespace GameConstants
{
...
static const int InitialLoadingDelay = 2000;
...
}
// GameRenderer.cpp
task<void> GameRenderer::CreateGameDeviceResourcesAsync(_In_ Simple3DGame^ game)
{
std::vector<task<void>> tasks;
...
tasks.push_back(create_task([]()
{
wait(GameConstants::InitialLoadingDelay);
}));
...
}
concurrency::wait öğesinin C++/WinRT sürümü, winrt::resume_after yapısıdır. Bu yapıyı bir PPL görevi içinde oluşturabiliriz co_await . İşte bir kod örneği.
// GameConstants.h
namespace GameConstants
{
using namespace std::literals::chrono_literals;
...
static const auto InitialLoadingDelay = 2000ms;
...
}
// GameRenderer.cpp
task<void> GameRenderer::CreateGameDeviceResourcesAsync(_In_ Simple3DGame^ game)
{
std::vector<task<void>> tasks;
...
tasks.push_back(create_task([]() -> task<void>
{
co_await winrt::resume_after(GameConstants::InitialLoadingDelay);
}));
...
}
Yapmamız gereken diğer iki değişikliğe dikkat edin. GameConstants::InitialLoadingDelay türünü std::chrono::d uration olarak değiştirdik ve derleyici artık bunu çıkaramadığından lambda işlevinin dönüş türünü açık hale getirdik.
task<void> dönüş türünü winrt::IAsyncXxx'e geçirme
Simple3DGame::LoadLevelAsync
Simple3DGameDX ile çalışmamızın bu aşamasında, projede Simple3DGame::LoadLevelAsync işlevini çağıran tüm yerler, bunu çağırmak için co_await kullanır.
Başka bir deyişle, bu yöntemin dönüş türünü void< görevi> yerine winrt::Windows::Foundation::IAsyncAction olarak değiştirebiliriz (geri kalanını değiştirmeden bırakabiliriz).
winrt::Windows::Foundation::IAsyncAction Simple3DGame::LoadLevelAsync()
{
m_level[m_currentLevel]->Initialize(m_objects);
m_levelDuration = m_level[m_currentLevel]->TimeLimit() + m_levelBonusTime;
co_return co_await m_renderer->LoadLevelResourcesAsync();
}
Artık bu yöntemin geri kalanını ve bağımlılıklarını ( m_level gibi) C++/WinRT'ye taşımanız oldukça mekanik olmalıdır.
GameRenderer::LoadLevelResourcesAsync
İşte GameRenderer::LoadLevelResourcesAsync'in özgün C++/CX sürümü.
// GameConstants.h
namespace GameConstants
{
...
static const int LevelLoadingDelay = 500;
...
}
// GameRenderer.cpp
task<void> GameRenderer::LoadLevelResourcesAsync()
{
m_levelResourcesLoaded = false;
return create_task([this]()
{
wait(GameConstants::LevelLoadingDelay);
});
}
Simple3DGame::LoadLevelAsync , projede GameRenderer::LoadLevelResourcesAsync'i çağıran tek yerdir ve zaten bunu çağırmak için kullanır co_await .
Dolayısıyla artık GameRenderer::LoadLevelResourcesAsync'in bir görevi döndürmesine gerek yoktur; bunun yerine winrt::Windows::Foundation::IAsyncAction döndürebilir. Uygulamanın kendisi de tamamen C++/WinRT'ye bağlantı noktası oluşturmak için yeterlidir. Bu, Port concurrency::wait ile co_await winrt::resume_after içinde yaptığımız değişikliğin aynısını yapmayı gerektirir. Ayrıca projenin geri kalanında endişelenecek önemli bağımlılıklar yoktur.
Bu nedenle, yöntemin tamamen C++/WinRT'ye taşımasının ardından nasıl göründüğü aşağıda açıklanmalı.
// GameConstants.h
namespace GameConstants
{
using namespace std::literals::chrono_literals;
...
static const auto LevelLoadingDelay = 500ms;
...
}
// GameRenderer.cpp
winrt::Windows::Foundation::IAsyncAction GameRenderer::LoadLevelResourcesAsync()
{
m_levelResourcesLoaded = false;
co_return co_await winrt::resume_after(GameConstants::LevelLoadingDelay);
}
Hedef: bir yöntemi C++/WinRT'ye tam olarak taşıma
Bu anlatımı, BasicReaderWriter::ReadDataAsync yöntemini C++/WinRT'ye tamamen geçirerek nihai hedefin bir örneğiyle tamamlayalım.
Bu yönteme en son baktığımızda (ReadDataAsync'i (büyük ölçüde) C++/WinRT'ye taşıma, projenin geri kalanını değiştirmeden bölümünde), büyük ölçüde C++/WinRT'ye taşınmıştı. Ancak yine de Platform::Array<bayt>^ görevini döndürdü.
task<Platform::Array<byte>^> BasicReaderWriter::ReadDataAsync(
_In_ Platform::String^ filename)
{
auto location_from_cx = from_cx<winrt::Windows::Storage::StorageFolder>(m_location);
auto file = co_await location_from_cx.GetFileAsync(filename->Data());
auto buffer = co_await winrt::Windows::Storage::FileIO::ReadBufferAsync(file);
byte* bytes;
auto byteAccess = buffer.as<Windows::Storage::Streams::IBufferByteAccess>();
winrt::check_hresult(byteAccess->Buffer(&bytes));
co_return ref new Platform::Array<byte>(bytes, buffer.Length());
}
Bir görev döndürmek yerine, bunu IAsyncOperation döndürecek şekilde değiştireceğiz. Ve bu IAsyncOperation aracılığıyla bir bayt dizisi döndürmek yerine bir C++/WinRT IBuffer nesnesi döndüreceğiz. Bu, göreceğimiz gibi arama sitelerinde kodda küçük bir değişiklik yapılmasını da gerektirir.
C++/WinRT söz dizimi ve nesnelerini kullanmak için yönteminin uygulaması, parametresi ve m_location veri üyesini taşıma işleminden sonra nasıl görüneceği aşağıda gösterilmiştir.
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::Streams::IBuffer>
BasicReaderWriter::ReadDataAsync(
_In_ winrt::hstring const& filename)
{
StorageFile file{ co_await m_location.GetFileAsync(filename) };
co_return co_await FileIO::ReadBufferAsync(file);
}
winrt::array_view<byte> BasicLoader::GetBufferView(
winrt::Windows::Storage::Streams::IBuffer const& buffer)
{
byte* bytes;
auto byteAccess = buffer.as<Windows::Storage::Streams::IBufferByteAccess>();
winrt::check_hresult(byteAccess->Buffer(&bytes));
return { bytes, bytes + buffer.Length() };
}
Gördüğünüz gibi, arabellekten baytları alan eşzamanlı mantığı ayrı bir yönteme taşıdığımız için BasicReaderWriter::ReadDataAsync yönteminin kendisi çok daha basittir.
Ancak şimdi çağrı sitelerini C++/CX'te bu tür bir yapıdan taşımamız gerekiyor.
task<void> BasicLoader::LoadTextureAsync(...)
{
return m_basicReaderWriter->ReadDataAsync(filename).then(
[=](const Platform::Array<byte>^ textureData)
{
CreateTexture(...);
});
}
C++/WinRT'de bu desene.
winrt::Windows::Foundation::IAsyncAction BasicLoader::LoadTextureAsync(...)
{
auto textureBuffer = co_await m_basicReaderWriter.ReadDataAsync(filename);
auto textureData = GetBufferView(textureBuffer);
CreateTexture(...);
}
Önemli API'ler
- Iasyncaction
- IAsyncActionWithProgress<TProgress>
- IAsyncOperation<TResult>
- IAsyncOperationWithProgress<TResult, TProgress>
- implements::get_strong
- eşzamanlılık::create_async
- eşzamanlılık::create_task
- eşzamanlılık::task
- concurrency::task::then
- eşzamanlılık::wait
- winrt::fire_and_forget
- winrt::make_self
İlgili konular
Windows developer