Coroutine: görevler için bir zamanlayıcı – Bölüm 2, Dian-Lun Li

Adanali

Active member
Coroutine: görevler için bir zamanlayıcı – Bölüm 2, Dian-Lun Li
Bu blog yazısı, bir planlayıcı hakkındaki mini serinin ikinci kısmıdır ve önceki “Yazılım Geliştirme: Dian-Lun Li’nin Coroutines’e Kompakt Bir Giriş” makalesine dayanmaktadır.

Duyuru











C++ eşyordamları için tek iş parçacıklı bir zamanlayıcı



Bu bölümde eşyordamları planlamak için tek iş parçacıklı bir zamanlayıcı uyguluyorum. Arayüzle başlayalım:


Task TaskA(Scheduler& sch) {
std::cout << "Hello from TaskAn";&#13;
co_await sch.suspend();&#13;
std::cout << "Executing the TaskAn";&#13;
co_await sch.suspend();&#13;
std::cout << "TaskA is finishedn";&#13;
}&#13;
&#13;
Task TaskB(Scheduler& sch) {&#13;
std::cout << "Hello from TaskBn";&#13;
co_await sch.suspend();&#13;
std::cout << "Executing the TaskBn";&#13;
co_await sch.suspend();&#13;
std::cout << "TaskB is finishedn";&#13;
}&#13;
&#13;
&#13;
int main() {&#13;
&#13;
Scheduler sch;&#13;
&#13;
sch.emplace(TaskA(sch).get_handle());&#13;
sch.emplace(TaskB(sch).get_handle());&#13;
&#13;
std::cout << "Start scheduling...n";&#13;
&#13;
sch.schedule();


Birlikte TaskA birlikte TaskB bunlar eşyordamlardır. İçinde mainişlevinde, bir zamanlayıcı oluşturuyorum ve iki görevi (ortak rutin tutamaçları) zamanlayıcıya ekliyorum. Sonra ararım schedule iki aktiviteyi planlamak. Görev, aşağıdaki şekilde tanımlanan bir Coroutine nesnesidir:


struct Task {&#13;
&#13;
struct promise_type {&#13;
std::suspend_always initial_suspend() noexcept { return {}; }&#13;
std::suspend_always final_suspend() noexcept { return {}; }&#13;
&#13;
Task get_return_object() { &#13;
return std::coroutine_handle<promise_type>::from_promise(*this); &#13;
}&#13;
void return_void() {}&#13;
void unhandled_exception() {}&#13;
};&#13;
&#13;
Task(std::coroutine_handle<promise_type> handle): handle{handle} {}&#13;
&#13;
auto get_handle() { return handle; }&#13;
&#13;
std::coroutine_handle<promise_type> handle;&#13;
};


Her iki fonksiyona da sahip olduğum unutulmamalıdır. initial_suspend hem de operasyonda final_suspend std::suspend_always geri dönmek. Bu gerekli çünkü eşyordamın tüm yürütülmesini zamanlayıcıya devretmek istiyorum. Ben gelene kadar eşyordamlar yürütülmez schedule Aramalar. Zamanlayıcı şu şekilde tanımlanır:


class Scheduler {&#13;
&#13;
//std::queue<std::coroutine_handle<>> _tasks;&#13;
std::stack<std::coroutine_handle<>> _tasks;&#13;
&#13;
public: &#13;
&#13;
void emplace(std::coroutine_handle<> task) {&#13;
_tasks.push(task);&#13;
}&#13;
&#13;
void schedule() {&#13;
while(!_tasks.empty()) {&#13;
//auto task = _tasks.front();&#13;
auto task = _tasks.top();&#13;
_tasks.pop();&#13;
task.resume();&#13;
&#13;
if(!task.done()) { &#13;
_tasks.push(task);&#13;
}&#13;
else {&#13;
task.destroy();&#13;
}&#13;
}&#13;
}&#13;
&#13;
auto suspend() {&#13;
return std::suspend_always{};&#13;
}&#13;
};


Zamanlayıcıda görevleri bir yığında saklıyorum ve dağıtıyorum emplace-Üye işlevi, kullanıcıların bir görevi yığına aktarabilmesini sağlar. İçinde schedule-Üyelik İşlevi Her zaman bir görevi yığından kaldırırım. Bir göreve devam ettiğimde görevin tamamlanıp tamamlanmadığını kontrol ederim. Değilse, daha sonra planlamak üzere görevi tekrar yığına iterim. Aksi halde bitmiş ödevi yok edeceğim. Programı çalıştırdıktan sonra aşağıdaki sonuçlar elde edilir:








Zamanlayıcı, görevleri depolamak için bir yığın (son giren ilk çıkar) kullanır. Yığını bir kuyrukla değiştirirsem (ilk giren ilk çıkar), görevlerin yürütülme sırası değişir:








Bütünlüğü sağlamak adına her iki program da burada tekrar özetlenmiştir:


// stackScheduler.cpp&#13;
&#13;
#include <coroutine>&#13;
#include <iostream>&#13;
#include <stack>&#13;
&#13;
&#13;
struct Task {&#13;
&#13;
struct promise_type {&#13;
std::suspend_always initial_suspend() noexcept { return {}; }&#13;
std::suspend_always final_suspend() noexcept { return {}; }&#13;
&#13;
Task get_return_object() { &#13;
return std::coroutine_handle<promise_type>::from_promise(*this); &#13;
}&#13;
void return_void() {}&#13;
void unhandled_exception() {}&#13;
};&#13;
&#13;
Task(std::coroutine_handle<promise_type> handle): handle{handle} {}&#13;
&#13;
auto get_handle() { return handle; }&#13;
&#13;
std::coroutine_handle<promise_type> handle;&#13;
};&#13;
&#13;
class Scheduler {&#13;
&#13;
std::stack<std::coroutine_handle<>> _tasks;&#13;
&#13;
public: &#13;
&#13;
void emplace(std::coroutine_handle<> task) {&#13;
_tasks.push(task);&#13;
}&#13;
&#13;
void schedule() {&#13;
while(!_tasks.empty()) {&#13;
auto task = _tasks.top();&#13;
_tasks.pop();&#13;
task.resume();&#13;
&#13;
if(!task.done()) { &#13;
_tasks.push(task);&#13;
}&#13;
else {&#13;
task.destroy();&#13;
}&#13;
}&#13;
}&#13;
&#13;
auto suspend() {&#13;
return std::suspend_always{};&#13;
}&#13;
};&#13;
&#13;
&#13;
Task TaskA(Scheduler& sch) {&#13;
std::cout << "Hello from TaskAn";&#13;
co_await sch.suspend();&#13;
std::cout << "Executing the TaskAn";&#13;
co_await sch.suspend();&#13;
std::cout << "TaskA is finishedn";&#13;
}&#13;
&#13;
Task TaskB(Scheduler& sch) {&#13;
std::cout << "Hello from TaskBn";&#13;
co_await sch.suspend();&#13;
std::cout << "Executing the TaskBn";&#13;
co_await sch.suspend();&#13;
std::cout << "TaskB is finishedn";&#13;
}&#13;
&#13;
&#13;
int main() {&#13;
&#13;
std::cout << 'n';&#13;
&#13;
Scheduler sch;&#13;
&#13;
sch.emplace(TaskA(sch).get_handle());&#13;
sch.emplace(TaskB(sch).get_handle());&#13;
&#13;
std::cout << "Start scheduling...n";&#13;
&#13;
sch.schedule();&#13;
&#13;
std::cout << 'n';&#13;
&#13;
}


// queueScheduler.cpp&#13;
&#13;
#include <coroutine>&#13;
#include <iostream>&#13;
#include <queue>&#13;
&#13;
&#13;
struct Task {&#13;
&#13;
struct promise_type {&#13;
std::suspend_always initial_suspend() noexcept { return {}; }&#13;
std::suspend_always final_suspend() noexcept { return {}; }&#13;
&#13;
Task get_return_object() { &#13;
return std::coroutine_handle<promise_type>::from_promise(*this); &#13;
}&#13;
void return_void() {}&#13;
void unhandled_exception() {}&#13;
};&#13;
&#13;
Task(std::coroutine_handle<promise_type> handle): handle{handle} {}&#13;
&#13;
auto get_handle() { return handle; }&#13;
&#13;
std::coroutine_handle<promise_type> handle;&#13;
};&#13;
&#13;
class Scheduler {&#13;
&#13;
std::queue<std::coroutine_handle<>> _tasks;&#13;
&#13;
public: &#13;
&#13;
void emplace(std::coroutine_handle<> task) {&#13;
_tasks.push(task);&#13;
}&#13;
&#13;
void schedule() {&#13;
while(!_tasks.empty()) {&#13;
auto task = _tasks.front();&#13;
_tasks.pop();&#13;
task.resume();&#13;
&#13;
if(!task.done()) { &#13;
_tasks.push(task);&#13;
}&#13;
else {&#13;
task.destroy();&#13;
}&#13;
}&#13;
}&#13;
&#13;
auto suspend() {&#13;
return std::suspend_always{};&#13;
}&#13;
};&#13;
&#13;
&#13;
Task TaskA(Scheduler& sch) {&#13;
std::cout << "Hello from TaskAn";&#13;
co_await sch.suspend();&#13;
std::cout << "Executing the TaskAn";&#13;
co_await sch.suspend();&#13;
std::cout << "TaskA is finishedn";&#13;
}&#13;
&#13;
Task TaskB(Scheduler& sch) {&#13;
std::cout << "Hello from TaskBn";&#13;
co_await sch.suspend();&#13;
std::cout << "Executing the TaskBn";&#13;
co_await sch.suspend();&#13;
std::cout << "TaskB is finishedn";&#13;
}&#13;
&#13;
&#13;
int main() {&#13;
&#13;
std::cout << 'n';&#13;
&#13;
Scheduler sch;&#13;
&#13;
sch.emplace(TaskA(sch).get_handle());&#13;
sch.emplace(TaskB(sch).get_handle());&#13;
&#13;
std::cout << "Start scheduling...n";&#13;
&#13;
sch.schedule();&#13;
&#13;
std::cout << 'n';&#13;
&#13;
}


Sıradaki ne?


Dian-Lun Li’nin bu makalesi eşyordamlar için basit bir zamanlayıcı gösteriyor. Bir sonraki yazımda daha ileri deneyler için Dian-Lun’un zamanlayıcısını kullanacağım.


(harita)



Haberin Sonu
 
Üst