Düzeltme: C ++ blogundaki choroutines için öncelik zamanlayıcıdaki hata
Son iki blog yazımda koro için bir öncelik zamanlayıcı sundum. Kodun bir hatası vardı.
Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.
İşte kusurlu zamanlayıcı böyle görünür:
// priority_queueSchedulerPriority.cpp
#include <concepts>
#include <coroutine>
#include <functional>
#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;
};
using job = std:
air<int, std::coroutine_handle<>>;
template <typename Updater = std::identity, // (1)
typename Comperator = std::ranges::less>
requires std::invocable<decltype(Updater()), int> && // (2)
std:
redicate<decltype(Comperator()), job, job>
class Scheduler {
std:
riority_queue<job, std::vector<job>, Comperator> _prioTasks;
public:
void emplace(int prio, std::coroutine_handle<> task) {
_prioTasks.push(std::make_pair(prio, task));
}
void schedule() {
Updater upd = {}; // (3)
while(!_prioTasks.empty()) {
auto [prio, task] = _prioTasks.top();
_prioTasks.pop();
task.resume();
if(!task.done()) {
_prioTasks.push(std::make_pair(upd(prio), task)); // (4)
}
else {
task.destroy();
}
}
}
};
Task createTask(const std::string& name) {
std::cout << name << " startn";
co_await std::suspend_always();
for (int i = 0; i <= 3; ++i ) {
std::cout << name << " execute " << i << "n"; // (5)
co_await std::suspend_always();
}
co_await std::suspend_always();
std::cout << name << " finishn";
}
int main() {
std::cout << 'n';
Scheduler scheduler1; // (6)
scheduler1.emplace(0, createTask("TaskA").get_handle());
scheduler1.emplace(1, createTask(" TaskB").get_handle());
scheduler1.emplace(2, createTask(" TaskC").get_handle());
scheduler1.schedule();
std::cout << 'n';
Scheduler<decltype([](int a) { return a - 1; })> scheduler2; // (7)
scheduler2.emplace(0, createTask("TaskA").get_handle());
scheduler2.emplace(1, createTask(" TaskB").get_handle());
scheduler2.emplace(2, createTask(" TaskC").get_handle());
scheduler2.schedule();
std::cout << 'n';
}
Bu, aldığım programın baskısıydı:
Christof Meerwald, GCC ile farklı bir baskı aldı. Bu öneri için teşekkürler. İşte etkinleştirilmiş optimizasyonlu GCC çıkışı.
Windows sürümü de yanlıştı:
İşte hatayla ilgili önemli çizgiler:
Task createTask(const std::string& name) { // (1)
std::cout << name << " startn";
co_await std::suspend_always();
for (int i = 0; i <= 3; ++i ) {
std::cout << name << " execute " << i << "n";
co_await std::suspend_always();
}
co_await std::suspend_always();
std::cout << name << " finishn";
}
int main() {
std::cout << 'n';
Scheduler scheduler1;
scheduler1.emplace(0, createTask("TaskA").get_handle()); // (2)
scheduler1.emplace(1, createTask(" TaskB").get_handle()); // (3)
scheduler1.emplace(2, createTask(" TaskC").get_handle());// (4)
scheduler1.schedule();
std::cout << 'n';
Scheduler<decltype([](int a) { return a - 1; })> scheduler2;
scheduler2.emplace(0, createTask("TaskA").get_handle()); // (5)
scheduler2.emplace(1, createTask(" TaskB").get_handle()); // (6)
scheduler2.emplace(2, createTask(" TaskC").get_handle());// (7)
scheduler2.schedule();
std::cout << 'n';
}
Koro createTask Dizenizi LValue Kostümüne (1) referans olarak kabul edin, ancak konularınız “TaskA" - "TaskC“Onlar rvalues (2 – 7). Geçici bir değişkene referans kullanmak belirsiz bir davranıştır. Diğer programlar priority_SchedulerSimplified VE priority_queueSchedulerComparator “C ++ Programlama Dili: Koro İçin Öncelik Zamanlayıcı” ve “C ++ Programlama Dili: Koro için Zorlu Bir Öncelik Zamanlayıcı” makalelerinde aynı soruna sahiptir.
Sorunun kaldırılması kolaydır. Ya da bakıcıyı alıyor createTask Konunuz değere göre (Task createTask(std::string name)) veya argümanları lvalori olur. İşte (1) – (3) 'deki ikinci yaklaşım:
// priority_queueSchedulerPriority.cpp
#include <concepts>
#include <coroutine>
#include <functional>
#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;
};
using job = std:
air<int, std::coroutine_handle<>>;
template <typename Updater = std::identity,
typename Comperator = std::ranges::less>
requires std::invocable<decltype(Updater()), int> &&
std:
redicate<decltype(Comperator()), job, job>
class Scheduler {
std:
riority_queue<job, std::vector<job>, Comperator> _prioTasks;
public:
void emplace(int prio, std::coroutine_handle<> task) {
_prioTasks.push(std::make_pair(prio, task));
}
void schedule() {
Updater upd = {};
while(!_prioTasks.empty()) {
auto [prio, task] = _prioTasks.top();
_prioTasks.pop();
task.resume();
if(!task.done()) {
_prioTasks.push(std::make_pair(upd(prio), task));
}
else {
task.destroy();
}
}
}
};
Task createTask(const std::string& name) {
std::cout << name << " startn";
co_await std::suspend_always();
for (int i = 0; i <= 3; ++i ) {
std::cout << name << " execute " << i << "n";
co_await std::suspend_always();
}
co_await std::suspend_always();
std::cout << name << " finishn";
}
int main() {
std::cout << 'n';
std::string taskA = "TaskA"; // (1)
std::string taskB = " TaskB"; // (2)
std::string taskC = " TaskC"; // (3)
Scheduler scheduler1;
scheduler1.emplace(0, createTask(taskA).get_handle());
scheduler1.emplace(1, createTask(taskB).get_handle());
scheduler1.emplace(2, createTask(taskC).get_handle());
scheduler1.schedule();
std::cout << 'n';
Scheduler<decltype([](int a) { return a - 1; })> scheduler2;
scheduler2.emplace(0, createTask(taskA).get_handle());
scheduler2.emplace(1, createTask(taskB).get_handle());
scheduler2.emplace(2, createTask(taskC).get_handle());
scheduler2.schedule();
std::cout << 'n';
}
Sırada ne var?
Koro, eşzamansız kod yazmak için sezgisel bir yol sunuyor. Bir sonraki yazım, tek bir üretici yapımcı-denetçi iş tabanlı iş akışı sunan Ljubic Damir'in konukları için bir yazı olacak.
(RME)
Son iki blog yazımda koro için bir öncelik zamanlayıcı sundum. Kodun bir hatası vardı.

Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.

İşte kusurlu zamanlayıcı böyle görünür:
// priority_queueSchedulerPriority.cpp
#include <concepts>
#include <coroutine>
#include <functional>
#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;
};
using job = std:
template <typename Updater = std::identity, // (1)
typename Comperator = std::ranges::less>
requires std::invocable<decltype(Updater()), int> && // (2)
std:
class Scheduler {
std:
public:
void emplace(int prio, std::coroutine_handle<> task) {
_prioTasks.push(std::make_pair(prio, task));
}
void schedule() {
Updater upd = {}; // (3)
while(!_prioTasks.empty()) {
auto [prio, task] = _prioTasks.top();
_prioTasks.pop();
task.resume();
if(!task.done()) {
_prioTasks.push(std::make_pair(upd(prio), task)); // (4)
}
else {
task.destroy();
}
}
}
};
Task createTask(const std::string& name) {
std::cout << name << " startn";
co_await std::suspend_always();
for (int i = 0; i <= 3; ++i ) {
std::cout << name << " execute " << i << "n"; // (5)
co_await std::suspend_always();
}
co_await std::suspend_always();
std::cout << name << " finishn";
}
int main() {
std::cout << 'n';
Scheduler scheduler1; // (6)
scheduler1.emplace(0, createTask("TaskA").get_handle());
scheduler1.emplace(1, createTask(" TaskB").get_handle());
scheduler1.emplace(2, createTask(" TaskC").get_handle());
scheduler1.schedule();
std::cout << 'n';
Scheduler<decltype([](int a) { return a - 1; })> scheduler2; // (7)
scheduler2.emplace(0, createTask("TaskA").get_handle());
scheduler2.emplace(1, createTask(" TaskB").get_handle());
scheduler2.emplace(2, createTask(" TaskC").get_handle());
scheduler2.schedule();
std::cout << 'n';
}
Bu, aldığım programın baskısıydı:

Christof Meerwald, GCC ile farklı bir baskı aldı. Bu öneri için teşekkürler. İşte etkinleştirilmiş optimizasyonlu GCC çıkışı.

Windows sürümü de yanlıştı:

İşte hatayla ilgili önemli çizgiler:
Task createTask(const std::string& name) { // (1)
std::cout << name << " startn";
co_await std::suspend_always();
for (int i = 0; i <= 3; ++i ) {
std::cout << name << " execute " << i << "n";
co_await std::suspend_always();
}
co_await std::suspend_always();
std::cout << name << " finishn";
}
int main() {
std::cout << 'n';
Scheduler scheduler1;
scheduler1.emplace(0, createTask("TaskA").get_handle()); // (2)
scheduler1.emplace(1, createTask(" TaskB").get_handle()); // (3)
scheduler1.emplace(2, createTask(" TaskC").get_handle());// (4)
scheduler1.schedule();
std::cout << 'n';
Scheduler<decltype([](int a) { return a - 1; })> scheduler2;
scheduler2.emplace(0, createTask("TaskA").get_handle()); // (5)
scheduler2.emplace(1, createTask(" TaskB").get_handle()); // (6)
scheduler2.emplace(2, createTask(" TaskC").get_handle());// (7)
scheduler2.schedule();
std::cout << 'n';
}
Koro createTask Dizenizi LValue Kostümüne (1) referans olarak kabul edin, ancak konularınız “TaskA" - "TaskC“Onlar rvalues (2 – 7). Geçici bir değişkene referans kullanmak belirsiz bir davranıştır. Diğer programlar priority_SchedulerSimplified VE priority_queueSchedulerComparator “C ++ Programlama Dili: Koro İçin Öncelik Zamanlayıcı” ve “C ++ Programlama Dili: Koro için Zorlu Bir Öncelik Zamanlayıcı” makalelerinde aynı soruna sahiptir.
Sorunun kaldırılması kolaydır. Ya da bakıcıyı alıyor createTask Konunuz değere göre (Task createTask(std::string name)) veya argümanları lvalori olur. İşte (1) – (3) 'deki ikinci yaklaşım:
// priority_queueSchedulerPriority.cpp
#include <concepts>
#include <coroutine>
#include <functional>
#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;
};
using job = std:
template <typename Updater = std::identity,
typename Comperator = std::ranges::less>
requires std::invocable<decltype(Updater()), int> &&
std:
class Scheduler {
std:
public:
void emplace(int prio, std::coroutine_handle<> task) {
_prioTasks.push(std::make_pair(prio, task));
}
void schedule() {
Updater upd = {};
while(!_prioTasks.empty()) {
auto [prio, task] = _prioTasks.top();
_prioTasks.pop();
task.resume();
if(!task.done()) {
_prioTasks.push(std::make_pair(upd(prio), task));
}
else {
task.destroy();
}
}
}
};
Task createTask(const std::string& name) {
std::cout << name << " startn";
co_await std::suspend_always();
for (int i = 0; i <= 3; ++i ) {
std::cout << name << " execute " << i << "n";
co_await std::suspend_always();
}
co_await std::suspend_always();
std::cout << name << " finishn";
}
int main() {
std::cout << 'n';
std::string taskA = "TaskA"; // (1)
std::string taskB = " TaskB"; // (2)
std::string taskC = " TaskC"; // (3)
Scheduler scheduler1;
scheduler1.emplace(0, createTask(taskA).get_handle());
scheduler1.emplace(1, createTask(taskB).get_handle());
scheduler1.emplace(2, createTask(taskC).get_handle());
scheduler1.schedule();
std::cout << 'n';
Scheduler<decltype([](int a) { return a - 1; })> scheduler2;
scheduler2.emplace(0, createTask(taskA).get_handle());
scheduler2.emplace(1, createTask(taskB).get_handle());
scheduler2.emplace(2, createTask(taskC).get_handle());
scheduler2.schedule();
std::cout << 'n';
}
Sırada ne var?
Koro, eşzamansız kod yazmak için sezgisel bir yol sunuyor. Bir sonraki yazım, tek bir üretici yapımcı-denetçi iş tabanlı iş akışı sunan Ljubic Damir'in konukları için bir yazı olacak.
(RME)