C++ Programlama Dili: Eşyordamlar için öncelikli zamanlayıcı
Bu, C++ eşyordamları için zamanlayıcılar hakkındaki mini dizimin üçüncü makalesi. İlk iki makale Dian-Lun Lin’in konuk gönderileriydi:
Duyuru
Rainer Grimm uzun yıllardır yazılım mimarı, ekip ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanıyor, aynı zamanda özel konferanslarda sık sık konuşmaktan da hoşlanıyor. Modern C++ adlı blogunda C++ tutkusunu yoğun bir şekilde ele alıyor.
Dian-Lun’un zamanlayıcıları std::stack ve std::queue konteyner adaptörünü temel alıyordu. THE std::stack Görevlerini son giren ilk çıkar stratejisine göre yürütür std::queue Ancak ilk giren ilk çıkar ilkesine göre.
Aşağıdaki kod parçacığı kuyruk tabanlı zamanlayıcıyı gösterir:
class Scheduler {
std::queue<std::coroutine_handle<>> _tasks;
public:
void emplace(std::coroutine_handle<> task) {
_tasks.push(task);
}
void schedule() {
while(!_tasks.empty()) {
auto task = _tasks.front();
_tasks.pop();
task.resume();
if(!task.done()) {
_tasks.push(task);
}
else {
task.destroy();
}
}
}
auto suspend() {
return std::suspend_always{};
}
};
Bu zamanlayıcıya öncelikler eklemek oldukça basittir.
Öncelik sıralarına dayalı bir zamanlayıcı
std:
riority_queue yakında std::stack VE std::queue C++98’deki üçüncü konteyner bağdaştırıcısı.
THE std:
riority_queue ve std::queue benzer. Temel fark, en büyük öğenin her zaman öncelik sırasının en üstünde yer almasıdır. std:
riority_queue karşılaştırma operatörü varsayılan olarak kullanılır std::less. THE Nachschlagezeit birinde std:
riority_queue sabittir ancak ekleme ve çıkarma logaritmiktir.
onu değiştireceğim std::queue önceki zamanlayıcıda -den -e std:
riority_queue:
// priority_queueScheduler.cpp
#include <coroutine>
#include <iostream>
#include <queue>
#include <utility>
struct Task {
struct promise_type {
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
Task get_return_object() {
return std::coroutine_handle<promise_type>::from_promise(*this);
}
void return_void() {}
void unhandled_exception() {}
};
Task(std::coroutine_handle<promise_type> handle): handle{handle}{}
auto get_handle() { return handle; }
std::coroutine_handle<promise_type> handle;
};
class Scheduler {
// (1)
std:
riority_queue<std:
air<int, std::coroutine_handle<>>> _prioTasks;
public:
void emplace(int prio, std::coroutine_handle<> task) { // (2)
_prioTasks.push(std::make_pair(prio, task));
}
void schedule() {
while(!_prioTasks.empty()) { // (3)
auto [prio, task] = _prioTasks.top();
_prioTasks.pop();
task.resume();
if(!task.done()) {
_prioTasks.push(std::make_pair(prio, task)); // (4)
}
else {
task.destroy();
}
}
}
auto suspend() {
return std::suspend_always{};
}
};
Task TaskA(Scheduler& sch) {
std::cout << "Hello from TaskAn";
co_await sch.suspend();
std::cout << "Executing the TaskAn";
co_await sch.suspend();
std::cout << "TaskA is finishedn";
}
Task TaskB(Scheduler& sch) {
std::cout << "Hello from TaskBn";
co_await sch.suspend();
std::cout << "Executing the TaskBn";
co_await sch.suspend();
std::cout << "TaskB is finishedn";
}
int main() {
std::cout << 'n';
Scheduler scheduler1;
scheduler1.emplace(0, TaskA(scheduler1).get_handle()); // (5)
scheduler1.emplace(1, TaskB(scheduler1).get_handle());
scheduler1.schedule();
std::cout << 'n';
Scheduler scheduler2;
scheduler2.emplace(1, TaskA(scheduler2).get_handle()); // (6)
scheduler2.emplace(0, TaskB(scheduler2).get_handle());
scheduler2.schedule();
std::cout << 'n';
}
İlk önce şunu kullanın: std:
riority_queue bir çift (öncelik, tutamaç) (1). Artık bu çift sahnede olacak. _prioTask (2) yerleştirildi. Zamanlayıcı çalışırken, _prioTask boştur (3), aksi takdirde ilk görev çağrılır, kaldırılır ve devam ettirilir. Görev tamamlanmazsa dosyaya iade edilecektir _prioTasks ertelendi (4).
Birinin kullanımı std:
riority_queue<std:
air<int, std::coroutine_handle<>>> en yüksek öncelikli görevlerin ilk önce yapılması gibi güzel bir yan etkiye sahiptir. Faaliyetlerin zamanlayıcıya eklenme sırası hiçbir fark yaratmaz (5 ve 6); önceliği 1 olan görev ilk önce çalıştırılır.
Bir sonraki yazımda öncelik yönetimini geliştirmeden önce öncelikle koroutini basitleştirmek istiyorum.
Basitleştirilmiş eşyordam
İşte önceki koroutinler TaskA VE TaskB:
Task TaskA(Scheduler& sch) {
std::cout << "Hello from TaskAn";
co_await sch.suspend();
std::cout << "Executing the TaskAn";
co_await sch.suspend();
std::cout << "TaskA is finishedn";
}
Task TaskB(Scheduler& sch) {
std::cout << "Hello from TaskBn";
co_await sch.suspend();
std::cout << "Executing the TaskBn";
co_await sch.suspend();
std::cout << "TaskB is finishedn";
}
Yerine co_await zamanlayıcıda, doğrudan varsayılan bekleyebilirliği çağırarak değiştiriyorum std::suspend_always. Zamanlayıcının üye askıya alma işlevini nasıl kaldırabileceğimi burada bulabilirsiniz. İkincisi: eşyordam görevine göre adlandırılmıştır:
Task createTask(const std::string& name) {
std::cout << name << " startn";
co_await std::suspend_always();
std::cout << name << " executen";
co_await std::suspend_always();
std::cout << name << " finishn";
}
Son olarak, karşılık gelen çıktıyı içeren basitleştirilmiş program burada.
// priority_queueSchedulerSimplified.cpp
#include <coroutine>
#include <iostream>
#include <queue>
#include <utility>
struct Task {
struct promise_type {
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
Task get_return_object() {
return std::coroutine_handle<promise_type>::from_promise(*this);
}
void return_void() {}
void unhandled_exception() {}
};
Task(std::coroutine_handle<promise_type> handle): handle{handle}{}
auto get_handle() { return handle; }
std::coroutine_handle<promise_type> handle;
};
class Scheduler {
std:
riority_queue<std:
air<int, std::coroutine_handle<>>> _prioTasks;
public:
void emplace(int prio, std::coroutine_handle<> task) {
_prioTasks.push(std::make_pair(prio, task));
}
void schedule() {
while(!_prioTasks.empty()) {
auto [prio, task] = _prioTasks.top();
_prioTasks.pop();
task.resume();
if(!task.done()) {
_prioTasks.push(std::make_pair(prio, task));
}
else {
task.destroy();
}
}
}
};
Task createTask(const std::string& name) {
std::cout << name << " startn";
co_await std::suspend_always();
std::cout << name << " executen";
co_await std::suspend_always();
std::cout << name << " finishn";
}
int main() {
std::cout << 'n';
Scheduler scheduler1;
scheduler1.emplace(0, createTask("TaskA").get_handle());
scheduler1.emplace(1, createTask(" TaskB").get_handle());
scheduler1.schedule();
std::cout << 'n';
Scheduler scheduler2;
scheduler2.emplace(1, createTask("TaskA").get_handle());
scheduler2.emplace(0, createTask(" TaskB").get_handle());
scheduler2.schedule();
std::cout << 'n';
}
Sıradaki ne?
Bir sonraki yazımda görev öncelik yönetimini daha da geliştireceğim.
(kendim)
Haberin Sonu
C++ Programlama Dili: Eşyordamlar için öncelikli zamanlayıcı
Bu, C++ eşyordamları için zamanlayıcılar hakkındaki mini dizimin üçüncü makalesi. İlk iki makale Dian-Lun Lin’in konuk gönderileriydi:
Duyuru

Rainer Grimm uzun yıllardır yazılım mimarı, ekip ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanıyor, aynı zamanda özel konferanslarda sık sık konuşmaktan da hoşlanıyor. Modern C++ adlı blogunda C++ tutkusunu yoğun bir şekilde ele alıyor.

Dian-Lun’un zamanlayıcıları std::stack ve std::queue konteyner adaptörünü temel alıyordu. THE std::stack Görevlerini son giren ilk çıkar stratejisine göre yürütür std::queue Ancak ilk giren ilk çıkar ilkesine göre.
Aşağıdaki kod parçacığı kuyruk tabanlı zamanlayıcıyı gösterir:
class Scheduler {
std::queue<std::coroutine_handle<>> _tasks;
public:
void emplace(std::coroutine_handle<> task) {
_tasks.push(task);
}
void schedule() {
while(!_tasks.empty()) {
auto task = _tasks.front();
_tasks.pop();
task.resume();
if(!task.done()) {
_tasks.push(task);
}
else {
task.destroy();
}
}
}
auto suspend() {
return std::suspend_always{};
}
};
Bu zamanlayıcıya öncelikler eklemek oldukça basittir.
Öncelik sıralarına dayalı bir zamanlayıcı
std:
THE std:
onu değiştireceğim std::queue önceki zamanlayıcıda -den -e std:
// priority_queueScheduler.cpp
#include <coroutine>
#include <iostream>
#include <queue>
#include <utility>
struct Task {
struct promise_type {
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
Task get_return_object() {
return std::coroutine_handle<promise_type>::from_promise(*this);
}
void return_void() {}
void unhandled_exception() {}
};
Task(std::coroutine_handle<promise_type> handle): handle{handle}{}
auto get_handle() { return handle; }
std::coroutine_handle<promise_type> handle;
};
class Scheduler {
// (1)
std:
public:
void emplace(int prio, std::coroutine_handle<> task) { // (2)
_prioTasks.push(std::make_pair(prio, task));
}
void schedule() {
while(!_prioTasks.empty()) { // (3)
auto [prio, task] = _prioTasks.top();
_prioTasks.pop();
task.resume();
if(!task.done()) {
_prioTasks.push(std::make_pair(prio, task)); // (4)
}
else {
task.destroy();
}
}
}
auto suspend() {
return std::suspend_always{};
}
};
Task TaskA(Scheduler& sch) {
std::cout << "Hello from TaskAn";
co_await sch.suspend();
std::cout << "Executing the TaskAn";
co_await sch.suspend();
std::cout << "TaskA is finishedn";
}
Task TaskB(Scheduler& sch) {
std::cout << "Hello from TaskBn";
co_await sch.suspend();
std::cout << "Executing the TaskBn";
co_await sch.suspend();
std::cout << "TaskB is finishedn";
}
int main() {
std::cout << 'n';
Scheduler scheduler1;
scheduler1.emplace(0, TaskA(scheduler1).get_handle()); // (5)
scheduler1.emplace(1, TaskB(scheduler1).get_handle());
scheduler1.schedule();
std::cout << 'n';
Scheduler scheduler2;
scheduler2.emplace(1, TaskA(scheduler2).get_handle()); // (6)
scheduler2.emplace(0, TaskB(scheduler2).get_handle());
scheduler2.schedule();
std::cout << 'n';
}
İlk önce şunu kullanın: std:
Birinin kullanımı std:

Bir sonraki yazımda öncelik yönetimini geliştirmeden önce öncelikle koroutini basitleştirmek istiyorum.
Basitleştirilmiş eşyordam
İşte önceki koroutinler TaskA VE TaskB:
Task TaskA(Scheduler& sch) {
std::cout << "Hello from TaskAn";
co_await sch.suspend();
std::cout << "Executing the TaskAn";
co_await sch.suspend();
std::cout << "TaskA is finishedn";
}
Task TaskB(Scheduler& sch) {
std::cout << "Hello from TaskBn";
co_await sch.suspend();
std::cout << "Executing the TaskBn";
co_await sch.suspend();
std::cout << "TaskB is finishedn";
}
Yerine co_await zamanlayıcıda, doğrudan varsayılan bekleyebilirliği çağırarak değiştiriyorum std::suspend_always. Zamanlayıcının üye askıya alma işlevini nasıl kaldırabileceğimi burada bulabilirsiniz. İkincisi: eşyordam görevine göre adlandırılmıştır:
Task createTask(const std::string& name) {
std::cout << name << " startn";
co_await std::suspend_always();
std::cout << name << " executen";
co_await std::suspend_always();
std::cout << name << " finishn";
}
Son olarak, karşılık gelen çıktıyı içeren basitleştirilmiş program burada.
// priority_queueSchedulerSimplified.cpp
#include <coroutine>
#include <iostream>
#include <queue>
#include <utility>
struct Task {
struct promise_type {
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
Task get_return_object() {
return std::coroutine_handle<promise_type>::from_promise(*this);
}
void return_void() {}
void unhandled_exception() {}
};
Task(std::coroutine_handle<promise_type> handle): handle{handle}{}
auto get_handle() { return handle; }
std::coroutine_handle<promise_type> handle;
};
class Scheduler {
std:
public:
void emplace(int prio, std::coroutine_handle<> task) {
_prioTasks.push(std::make_pair(prio, task));
}
void schedule() {
while(!_prioTasks.empty()) {
auto [prio, task] = _prioTasks.top();
_prioTasks.pop();
task.resume();
if(!task.done()) {
_prioTasks.push(std::make_pair(prio, task));
}
else {
task.destroy();
}
}
}
};
Task createTask(const std::string& name) {
std::cout << name << " startn";
co_await std::suspend_always();
std::cout << name << " executen";
co_await std::suspend_always();
std::cout << name << " finishn";
}
int main() {
std::cout << 'n';
Scheduler scheduler1;
scheduler1.emplace(0, createTask("TaskA").get_handle());
scheduler1.emplace(1, createTask(" TaskB").get_handle());
scheduler1.schedule();
std::cout << 'n';
Scheduler scheduler2;
scheduler2.emplace(1, createTask("TaskA").get_handle());
scheduler2.emplace(0, createTask(" TaskB").get_handle());
scheduler2.schedule();
std::cout << 'n';
}

Sıradaki ne?
Bir sonraki yazımda görev öncelik yönetimini daha da geliştireceğim.
(kendim)
Haberin Sonu