添加任务系统(TaskSystem)支持多线程任务处理和主线程回调 扩展Actor类添加更新监听器功能 新增GameWorld和GameTown场景类 添加多种混合模式(BlendMode)支持 实现异步资源加载界面和流程
143 lines
4.3 KiB
C++
143 lines
4.3 KiB
C++
#pragma once
|
|
|
|
#include <atomic>
|
|
#include <condition_variable>
|
|
#include <exception>
|
|
#include <future>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <queue>
|
|
#include <thread>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <frostbite2D/types/type_alias.h>
|
|
|
|
namespace frostbite2D {
|
|
|
|
struct TaskSystemConfig {
|
|
uint32 workerCount = 0;
|
|
uint32 maxMainThreadCallbacksPerFrame = 0;
|
|
uint32 threadStackSize = 0;
|
|
};
|
|
|
|
class TaskSystem {
|
|
public:
|
|
static TaskSystem& get();
|
|
|
|
TaskSystem(const TaskSystem&) = delete;
|
|
TaskSystem& operator=(const TaskSystem&) = delete;
|
|
|
|
bool init(const TaskSystemConfig& config = {});
|
|
void shutdown();
|
|
|
|
bool isInitialized() const { return initialized_; }
|
|
bool isMainThread() const;
|
|
size_t workerCount() const;
|
|
uint32 maxMainThreadCallbacksPerFrame() const;
|
|
|
|
template <typename F>
|
|
void post(F&& task) {
|
|
enqueueWorkerTask(Function<void()>(std::forward<F>(task)));
|
|
}
|
|
|
|
template <typename F>
|
|
auto submit(F&& task) -> std::future<std::invoke_result_t<F>> {
|
|
using Result = std::invoke_result_t<F>;
|
|
|
|
auto packagedTask =
|
|
std::make_shared<std::packaged_task<Result()>>(std::forward<F>(task));
|
|
std::future<Result> future = packagedTask->get_future();
|
|
|
|
enqueueWorkerTask([packagedTask]() mutable { (*packagedTask)(); });
|
|
return future;
|
|
}
|
|
|
|
template <typename F>
|
|
void postToMainThread(F&& callback) {
|
|
enqueueMainThreadTask(Function<void()>(std::forward<F>(callback)));
|
|
}
|
|
|
|
template <typename Task, typename OnComplete>
|
|
void submitThen(Task&& task, OnComplete&& onComplete) {
|
|
using Result = std::invoke_result_t<Task>;
|
|
using Storage = std::decay_t<Result>;
|
|
|
|
auto taskFn = std::make_shared<std::decay_t<Task>>(std::forward<Task>(task));
|
|
auto completeFn =
|
|
std::make_shared<std::decay_t<OnComplete>>(std::forward<OnComplete>(onComplete));
|
|
|
|
enqueueWorkerTask([this, taskFn, completeFn]() mutable {
|
|
try {
|
|
if constexpr (std::is_void_v<Result>) {
|
|
std::invoke(*taskFn);
|
|
enqueueMainThreadTask([completeFn]() mutable { std::invoke(*completeFn); });
|
|
} else {
|
|
auto result = std::make_shared<Storage>(std::invoke(*taskFn));
|
|
enqueueMainThreadTask([completeFn, result]() mutable {
|
|
std::invoke(*completeFn, std::move(*result));
|
|
});
|
|
}
|
|
} catch (...) {
|
|
logUnhandledTaskException(std::current_exception());
|
|
}
|
|
});
|
|
}
|
|
|
|
template <typename Task, typename OnComplete, typename OnError>
|
|
void submitThen(Task&& task, OnComplete&& onComplete, OnError&& onError) {
|
|
using Result = std::invoke_result_t<Task>;
|
|
using Storage = std::decay_t<Result>;
|
|
|
|
auto taskFn = std::make_shared<std::decay_t<Task>>(std::forward<Task>(task));
|
|
auto completeFn =
|
|
std::make_shared<std::decay_t<OnComplete>>(std::forward<OnComplete>(onComplete));
|
|
auto errorFn =
|
|
std::make_shared<std::decay_t<OnError>>(std::forward<OnError>(onError));
|
|
|
|
enqueueWorkerTask([this, taskFn, completeFn, errorFn]() mutable {
|
|
try {
|
|
if constexpr (std::is_void_v<Result>) {
|
|
std::invoke(*taskFn);
|
|
enqueueMainThreadTask([completeFn]() mutable { std::invoke(*completeFn); });
|
|
} else {
|
|
auto result = std::make_shared<Storage>(std::invoke(*taskFn));
|
|
enqueueMainThreadTask([completeFn, result]() mutable {
|
|
std::invoke(*completeFn, std::move(*result));
|
|
});
|
|
}
|
|
} catch (...) {
|
|
auto error = std::make_shared<std::exception_ptr>(std::current_exception());
|
|
enqueueMainThreadTask([errorFn, error]() mutable { std::invoke(*errorFn, *error); });
|
|
}
|
|
});
|
|
}
|
|
|
|
void drainMainThreadTasks(uint32 maxTasks = 0);
|
|
|
|
private:
|
|
TaskSystem() = default;
|
|
~TaskSystem();
|
|
|
|
void enqueueWorkerTask(Function<void()> task);
|
|
void enqueueMainThreadTask(Function<void()> task);
|
|
size_t resolveWorkerCount(uint32 requestedCount) const;
|
|
static void logUnhandledTaskException(std::exception_ptr error);
|
|
|
|
TaskSystemConfig config_;
|
|
std::vector<std::thread> workers_;
|
|
std::queue<Function<void()>> workerTasks_;
|
|
std::queue<Function<void()>> mainThreadTasks_;
|
|
|
|
mutable std::mutex workerMutex_;
|
|
mutable std::mutex mainThreadMutex_;
|
|
std::condition_variable workerCondition_;
|
|
|
|
std::atomic<bool> initialized_ = false;
|
|
std::atomic<bool> acceptingTasks_ = false;
|
|
std::thread::id mainThreadId_;
|
|
};
|
|
|
|
} // namespace frostbite2D
|