Ich würde gerne eine Klasse schreiben, die std :: thread umschließt und sich wie ein std :: thread verhält, aber eigentlich nicht jedes Mal einen Thread zuweist, wenn ich etwas asynchron verarbeiten muss. Der Grund ist, dass ich Multithreading in einem Kontext verwenden muss, in dem ich nicht dynamisch zuweisen darf, und ich möchte auch nicht den Overhead haben, einen std :: thread zu erstellen.Einen Thread schreiben, der am Leben bleibt
Stattdessen möchte ich, dass ein Thread in einer Schleife ausgeführt wird, und warten, bis die Verarbeitung beginnen kann. Der Client ruft invoke
auf, was den Thread aufweckt. Der Thread sperrt einen Mutex, verarbeitet ihn und schläft wieder ein. Eine Funktion join
verhält sich wie std :: thread :: join durch Sperren, bis der Thread die Sperre freigibt (d. H. Wieder einschläft).
Ich denke, ich habe die Klasse zum Laufen bekommen, aber wegen der allgemeinen mangelnden Erfahrung im Multi-Threading möchte ich fragen, ob irgendjemand Race Conditions erkennen kann oder ob der von mir verwendete Ansatz als "guter Stil" angesehen wird. Zum Beispiel bin ich mir nicht sicher, ob das temporäre Sperren des Mutex eine vernünftige Möglichkeit ist, dem Thread beizutreten.
EDIT ich ein anderes Rennen Zustand gefunden: wenn join
direkt nach invoke
Aufruf, gibt es keinen Grund, der Thread bereits den Mutex gesperrt und verriegelt so den Anrufer von join
bis der Faden schlafen geht. Um dies zu verhindern, musste ich eine Überprüfung für den Aufrufzähler hinzufügen.
Kopf
#pragma once
#include <thread>
#include <atomic>
#include <mutex>
class PersistentThread
{
public:
PersistentThread();
~PersistentThread();
// set function to invoke
// locks if thread is currently processing _func
void set(const std::function<void()> &f);
// wakes the thread up to process _func and fall asleep again
// locks if thread is currently processing _func
void invoke();
// mimics std::thread::join
// locks until the thread is finished with it's loop
void join();
private:
// intern thread loop
void loop(bool *initialized);
private:
bool _shutdownRequested{ false };
std::mutex _mutex;
std::unique_ptr<std::thread> _thread;
std::condition_variable _cond;
std::function<void()> _func{ nullptr };
};
Quelldatei
#include "PersistentThread.h"
PersistentThread::PersistentThread()
{
auto lock = std::unique_lock<std::mutex>(_mutex);
bool initialized = false;
_thread = std::make_unique<std::thread>(&PersistentThread::loop, this, &initialized);
// wait until _thread notifies, check bool initialized to prevent spurious wakeups
_cond.wait(lock, [&] {return initialized; });
}
PersistentThread::~PersistentThread()
{
{
std::lock_guard<std::mutex> lock(_mutex);
_func = nullptr;
_shutdownRequested = true;
// wake up and let join
_cond.notify_one();
}
// join thread,
if (_thread->joinable())
{
_thread->join();
}
}
void PersistentThread::set(const std::function<void()>& f)
{
std::lock_guard<std::mutex> lock(_mutex);
this->_func = f;
}
void PersistentThread::invoke()
{
std::lock_guard<std::mutex> lock(_mutex);
_cond.notify_one();
}
void PersistentThread::join()
{
bool joined = false;
while (!joined)
{
std::lock_guard<std::mutex> lock(_mutex);
joined = (_invokeCounter == 0);
}
}
void PersistentThread::loop(bool *initialized)
{
std::unique_lock<std::mutex> lock(_mutex);
*initialized = true;
_cond.notify_one();
while (true)
{
// wait until we get the mutex again
_cond.wait(lock, [this] {return _shutdownRequested || (this->_invokeCounter > 0); });
// shut down if requested
if (_shutdownRequested) return;
// process
if (_func) _func();
_invokeCounter--;
}
}
Klingt so, als ob Sie einen [thread pool] möchten (https://en.wikipedia.org/wiki/Thread_pool) – NathanOliver
Vielleicht sollte das auf Codereview SE gepostet werden? –
@NathanOliver Ich zweite das. Einfach ein paar Threads auf einmal erstellen und sie dann verwenden. – Carcigenicate