Task::WaitComplete to avoid spinning
Mutex waiting for task complete. Waiting for a task to be finished, The destructor for the thread and thread pool now take less than 1% usage. 😎
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
@@ -13,10 +14,18 @@ namespace MultiThreading {
|
||||
|
||||
class MultiThreading::TaskBase {
|
||||
protected:
|
||||
std::condition_variable completed_condition;
|
||||
std::atomic<bool> complete = false;
|
||||
std::mutex mtx;
|
||||
void MarkTaskComplete() { std::lock_guard<std::mutex> lock(mtx); complete = true; completed_condition.notify_all(); }
|
||||
public:
|
||||
/// @returns whether the task is finished.
|
||||
[[nodiscard]] bool Complete() const { return complete; }
|
||||
|
||||
/// Condition variable waits for the task to be complete
|
||||
/// @note There is no way to know if the task has been assigned, If you do this and that never happens the calling thread will wait forever.
|
||||
void WaitComplete() { std::unique_lock<std::mutex> lock(mtx); completed_condition.wait(lock, [this]() { return complete.load(); }); }
|
||||
|
||||
virtual void Run() = 0;
|
||||
public:
|
||||
TaskBase() = default;
|
||||
@@ -30,7 +39,7 @@ private:
|
||||
private:
|
||||
explicit Task(std::function<T()> callable, T* result = nullptr) : TaskBase(), result(result), callable(std::move(callable)) {}
|
||||
public:
|
||||
void Run() final { result ? *result = callable() : callable(); complete = true; }
|
||||
void Run() final { result ? *result = callable() : callable(); MarkTaskComplete(); }
|
||||
public:
|
||||
/// Create a Task. This is non-negotiable, This ensures that there's no issues related to the stack frame the task is on being popped before or while the job runs.
|
||||
/// @param callable The function to run, *usually a lambda or std::bind*
|
||||
@@ -47,7 +56,7 @@ private:
|
||||
std::function<void()> callable = nullptr;
|
||||
explicit Task(std::function<void()> callable) : TaskBase(), callable(std::move(callable)) {}
|
||||
public:
|
||||
void Run() final { callable(); complete = true; }
|
||||
void Run() final { callable(); MarkTaskComplete(); }
|
||||
|
||||
/// Create a Task. This is non-negotiable, This ensures that there's no issues related to the stack frame the task is on being popped before or while the job runs.
|
||||
/// @param callable The function to run, *usually a lambda or std::bind*
|
||||
|
@@ -1,9 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <condition_variable>
|
||||
#include <MultiThreading/Task.h>
|
||||
|
||||
namespace MultiThreading {
|
||||
@@ -16,8 +13,8 @@ private:
|
||||
std::atomic<bool> stop = false;
|
||||
std::condition_variable cv;
|
||||
std::thread worker;
|
||||
std::mutex mtx;
|
||||
bool busy = false;
|
||||
std::mutex mtx;
|
||||
private:
|
||||
void Runner();
|
||||
public:
|
||||
@@ -46,6 +43,5 @@ public:
|
||||
/// Crete a thread which will immediately run a task and then wait for another.
|
||||
explicit Thread(const std::function<void()>& task);
|
||||
/// Waits for the current task to finish (if there is one) and destroys the thread.
|
||||
// TODO Avoid spinning.
|
||||
~Thread();
|
||||
};
|
@@ -3,6 +3,7 @@
|
||||
#include <MultiThreading/Thread.h>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
namespace MultiThreading {
|
||||
class ThreadPool;
|
||||
}
|
||||
|
2
main.cpp
2
main.cpp
@@ -47,7 +47,7 @@ int main() {
|
||||
srand(time(nullptr));
|
||||
|
||||
int32_t task_completion_count = 0;
|
||||
auto* thread_pool = new ThreadPool();
|
||||
auto* thread_pool = new ThreadPool(4);
|
||||
|
||||
for (unsigned int i = 0; i < 128; i++) {
|
||||
auto some_task = Task<int32_t>::Create(([i] { return some_test_func(i + 1); }), &task_completion_count);
|
||||
|
@@ -5,7 +5,7 @@ using namespace MultiThreading;
|
||||
|
||||
Thread::~Thread() {
|
||||
if (current_task)
|
||||
while (!current_task->Complete()) {}
|
||||
current_task->WaitComplete();
|
||||
stop = true;
|
||||
|
||||
cv.notify_one();
|
||||
|
Reference in New Issue
Block a user