<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Threadpool mit abbrechbaren Jobs und then-Funktion]]></title><description><![CDATA[<p>Hi,</p>
<p>ich habe basierend auf boost::asio einen einfachen Threadpool aufgebaut, bei dem Jobs abbrechbar sein sollen. Basierend auf einer Empfehlung von Finnegan ( <a href="https://www.c-plusplus.net/forum/p2506141#2506141">https://www.c-plusplus.net/forum/p2506141#2506141</a> ) nutze ich dazu ein thread-safe abort-Flag, das ich über <code>shared_ptr&lt;atomic&lt;bool&gt;&gt;</code> implementiere.</p>
<p>Eine zusätzliche Anforderung an meinen Threadpool sollte jedoch sein, dass beim Fertigstellen eines Jobs eine then-Funktion aufgerufen wird. Diese erhält als Parameter ein Handle (weil dieselbe then-Funktion mit mehreren Jobs aufgerufen werden kann und ich über das Handle auch als Identifier dient) und den Rückgabewert der eigentlichen Job-Funktion. Zurzeit muss es ein Result geben und man muss eine then-Funktion angeben, das ist für mich aber okay.</p>
<p>Wenn ich mehrere Jobs hinzufüge, welche dieselbe then-Funktion nutzen, kann ich über eine Methode des Handles vergleichen, ob die übereinstimmen. Das mache ich, indem ich prüfe, ob die Zeiger übereinstimmen. Sollte der Job bereits aborted worden sein, returned diese Methode ebenfalls false, da ich das Ergebnis in dem Fall ignoriere.</p>
<p>Hier ist der (kompilierbare Code):</p>
<pre><code class="language-cpp">#include &lt;boost/asio.hpp&gt;
#include &lt;boost/thread.hpp&gt;
#include &lt;memory&gt;
#include &lt;atomic&gt;

class Handle
{
public:
	using SharedPtrBool = std::shared_ptr&lt;std::atomic&lt;bool&gt;&gt;;

	Handle() : abort(new std::atomic&lt;bool&gt;(false)) {}

	// checks whether handles are equal and whether both are not aborted
	bool EqualAndUnaborted(const Handle&amp; rhs) const
	{
		// any abort flag equal to true?
		if (*abort)
			return false;

		// do pointers equal?
		return abort.get() == rhs.abort.get();
	}

	// aborts thread
	void Abort()
	{
		*abort = true;
	}

	// get identifier
	int GetIdentifier() const
	{
		static_assert(sizeof(int) == sizeof(decltype(abort.get())), &quot;identifier type (int) size is not equal to pointer size&quot;);
		return reinterpret_cast&lt;int&gt;(abort.get());
	}

private:
	SharedPtrBool abort;

	friend class ThreadPool;
};

class ThreadPool
{
public:
	using SharedPtrBool = Handle::SharedPtrBool;

	ThreadPool(int threads)
		: work(ioService)
	{
		for (int n = 0; n &lt; threads; ++n)
			threadPool.create_thread(boost::bind(&amp;boost::asio::io_service::run, &amp;ioService));
	}

	~ThreadPool()
	{
		ioService.stop();
		threadPool.join_all();
	}

	template&lt;typename T, typename U&gt;
	Handle AddJob(T func, U then)
	{
		std::cout &lt;&lt; &quot;Job added&quot; &lt;&lt; std::endl;
		Handle h;
		ioService.post([h, func, then](){
			then(h, func(h.abort));
		});

		return h;
	}

	ThreadPool(const ThreadPool&amp;) = delete;
	ThreadPool&amp; operator=(const ThreadPool&amp;) = delete;
	ThreadPool(ThreadPool&amp;&amp;) = delete;
	ThreadPool&amp; operator=(ThreadPool&amp;&amp;) = delete;

private:
	boost::asio::io_service ioService;
	boost::thread_group threadPool;
	boost::asio::io_service::work work;
};
</code></pre>
<p>Folgendes Beispielprogramm funktioniert auch, wenn man es direkt unter obigen Code hängt:</p>
<pre><code class="language-cpp">Handle h1, h2, h3;

void done(Handle h, int r)
{
	if (h.EqualAndUnaborted(h1))
	{
		std::cout &lt;&lt; &quot;First with &quot; &lt;&lt; r &lt;&lt; std::endl;
	}
	else if (h.EqualAndUnaborted(h2))
	{
		std::cout &lt;&lt; &quot;Second with &quot; &lt;&lt; r &lt;&lt; std::endl;
	}
	else if (h.EqualAndUnaborted(h3))
	{
		std::cout &lt;&lt; &quot;Third with &quot; &lt;&lt; r &lt;&lt; std::endl;
	}
}

int main()
{
	ThreadPool pool(4);

	h1 = pool.AddJob([](ThreadPool::SharedPtrBool abort){
		int m = 0;
		for (int n = 0; n &lt; 999999999 &amp;&amp; !*abort; ++n)
			if (n % 1500 == 0)
				++m;
		return m;
	}, done);

	h2 = pool.AddJob([](ThreadPool::SharedPtrBool abort){
		int m = 0;
		for (int n = 0; n &lt; 9999999 &amp;&amp; !*abort; ++n)
			if (n % 2000 == 0)
				++m;
		return m;
	}, done);

	h3 = pool.AddJob([](ThreadPool::SharedPtrBool abort){
		int m = 0;
		for (int n = 0; n &lt; 9999999 &amp;&amp; !*abort; ++n)
			if (n % 1900 == 0)
				++m;
		return m;
	}, done);

	boost::this_thread::sleep_for(boost::chrono::milliseconds(1500));

	h1.Abort();

	std::cout &lt;&lt; &quot;aborted&quot; &lt;&lt; std::endl;

	boost::this_thread::sleep_for(boost::chrono::milliseconds(1500));
}
</code></pre>
<p>Meine Frage ist jetzt: Habe ich irgendwas übersehen, was mir die Threadsafety kaputt machen könnte oder zu anderen Problemen führen kann?</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/339378/threadpool-mit-abbrechbaren-jobs-und-then-funktion</link><generator>RSS for Node</generator><lastBuildDate>Sun, 12 Apr 2026 04:14:18 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/339378.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 25 Aug 2016 09:19:01 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Threadpool mit abbrechbaren Jobs und then-Funktion on Thu, 25 Aug 2016 09:23:07 GMT]]></title><description><![CDATA[<p>Hi,</p>
<p>ich habe basierend auf boost::asio einen einfachen Threadpool aufgebaut, bei dem Jobs abbrechbar sein sollen. Basierend auf einer Empfehlung von Finnegan ( <a href="https://www.c-plusplus.net/forum/p2506141#2506141">https://www.c-plusplus.net/forum/p2506141#2506141</a> ) nutze ich dazu ein thread-safe abort-Flag, das ich über <code>shared_ptr&lt;atomic&lt;bool&gt;&gt;</code> implementiere.</p>
<p>Eine zusätzliche Anforderung an meinen Threadpool sollte jedoch sein, dass beim Fertigstellen eines Jobs eine then-Funktion aufgerufen wird. Diese erhält als Parameter ein Handle (weil dieselbe then-Funktion mit mehreren Jobs aufgerufen werden kann und ich über das Handle auch als Identifier dient) und den Rückgabewert der eigentlichen Job-Funktion. Zurzeit muss es ein Result geben und man muss eine then-Funktion angeben, das ist für mich aber okay.</p>
<p>Wenn ich mehrere Jobs hinzufüge, welche dieselbe then-Funktion nutzen, kann ich über eine Methode des Handles vergleichen, ob die übereinstimmen. Das mache ich, indem ich prüfe, ob die Zeiger übereinstimmen. Sollte der Job bereits aborted worden sein, returned diese Methode ebenfalls false, da ich das Ergebnis in dem Fall ignoriere.</p>
<p>Hier ist der (kompilierbare Code):</p>
<pre><code class="language-cpp">#include &lt;boost/asio.hpp&gt;
#include &lt;boost/thread.hpp&gt;
#include &lt;memory&gt;
#include &lt;atomic&gt;

class Handle
{
public:
	using SharedPtrBool = std::shared_ptr&lt;std::atomic&lt;bool&gt;&gt;;

	Handle() : abort(new std::atomic&lt;bool&gt;(false)) {}

	// checks whether handles are equal and whether both are not aborted
	bool EqualAndUnaborted(const Handle&amp; rhs) const
	{
		// any abort flag equal to true?
		if (*abort)
			return false;

		// do pointers equal?
		return abort.get() == rhs.abort.get();
	}

	// aborts thread
	void Abort()
	{
		*abort = true;
	}

	// get identifier
	int GetIdentifier() const
	{
		static_assert(sizeof(int) == sizeof(decltype(abort.get())), &quot;identifier type (int) size is not equal to pointer size&quot;);
		return reinterpret_cast&lt;int&gt;(abort.get());
	}

private:
	SharedPtrBool abort;

	friend class ThreadPool;
};

class ThreadPool
{
public:
	using SharedPtrBool = Handle::SharedPtrBool;

	ThreadPool(int threads)
		: work(ioService)
	{
		for (int n = 0; n &lt; threads; ++n)
			threadPool.create_thread(boost::bind(&amp;boost::asio::io_service::run, &amp;ioService));
	}

	~ThreadPool()
	{
		ioService.stop();
		threadPool.join_all();
	}

	template&lt;typename T, typename U&gt;
	Handle AddJob(T func, U then)
	{
		std::cout &lt;&lt; &quot;Job added&quot; &lt;&lt; std::endl;
		Handle h;
		ioService.post([h, func, then](){
			then(h, func(h.abort));
		});

		return h;
	}

	ThreadPool(const ThreadPool&amp;) = delete;
	ThreadPool&amp; operator=(const ThreadPool&amp;) = delete;
	ThreadPool(ThreadPool&amp;&amp;) = delete;
	ThreadPool&amp; operator=(ThreadPool&amp;&amp;) = delete;

private:
	boost::asio::io_service ioService;
	boost::thread_group threadPool;
	boost::asio::io_service::work work;
};
</code></pre>
<p>Folgendes Beispielprogramm funktioniert auch, wenn man es direkt unter obigen Code hängt:</p>
<pre><code class="language-cpp">Handle h1, h2, h3;

void done(Handle h, int r)
{
	if (h.EqualAndUnaborted(h1))
	{
		std::cout &lt;&lt; &quot;First with &quot; &lt;&lt; r &lt;&lt; std::endl;
	}
	else if (h.EqualAndUnaborted(h2))
	{
		std::cout &lt;&lt; &quot;Second with &quot; &lt;&lt; r &lt;&lt; std::endl;
	}
	else if (h.EqualAndUnaborted(h3))
	{
		std::cout &lt;&lt; &quot;Third with &quot; &lt;&lt; r &lt;&lt; std::endl;
	}
}

int main()
{
	ThreadPool pool(4);

	h1 = pool.AddJob([](ThreadPool::SharedPtrBool abort){
		int m = 0;
		for (int n = 0; n &lt; 999999999 &amp;&amp; !*abort; ++n)
			if (n % 1500 == 0)
				++m;
		return m;
	}, done);

	h2 = pool.AddJob([](ThreadPool::SharedPtrBool abort){
		int m = 0;
		for (int n = 0; n &lt; 9999999 &amp;&amp; !*abort; ++n)
			if (n % 2000 == 0)
				++m;
		return m;
	}, done);

	h3 = pool.AddJob([](ThreadPool::SharedPtrBool abort){
		int m = 0;
		for (int n = 0; n &lt; 9999999 &amp;&amp; !*abort; ++n)
			if (n % 1900 == 0)
				++m;
		return m;
	}, done);

	boost::this_thread::sleep_for(boost::chrono::milliseconds(1500));

	h1.Abort();

	std::cout &lt;&lt; &quot;aborted&quot; &lt;&lt; std::endl;

	boost::this_thread::sleep_for(boost::chrono::milliseconds(1500));
}
</code></pre>
<p>Meine Frage ist jetzt: Habe ich irgendwas übersehen, was mir die Threadsafety kaputt machen könnte oder zu anderen Problemen führen kann?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2506500</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2506500</guid><dc:creator><![CDATA[Eisflamme]]></dc:creator><pubDate>Thu, 25 Aug 2016 09:23:07 GMT</pubDate></item><item><title><![CDATA[Reply to Threadpool mit abbrechbaren Jobs und then-Funktion on Thu, 25 Aug 2016 10:56:11 GMT]]></title><description><![CDATA[<p>ist ein shared_ptr&lt;atomic&lt;bool&gt;&gt; nicht overkill?</p>
<p>atomic&lt;bool&gt; ist doch schon dazu gedacht geteilt zu werden, warum noch mehrere atomic int counter drumrum machen?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2506507</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2506507</guid><dc:creator><![CDATA[kurze_frage]]></dc:creator><pubDate>Thu, 25 Aug 2016 10:56:11 GMT</pubDate></item><item><title><![CDATA[Reply to Threadpool mit abbrechbaren Jobs und then-Funktion on Thu, 25 Aug 2016 12:10:49 GMT]]></title><description><![CDATA[<p>Das eine ersetzt das andere ja nicht.</p>
<p>Denn ohne shared_ptr könnte ich ja keine Handles kopieren. Aber an wessen Scope könnte ich denn dann den bool binden? Wenn der Job bestimmt, wann der atomic&lt;bool&gt; stirbt, kann es sein, dass ich bei einem Abort aus dem Hauptthread ein bool auf false setze, welches nicht mehr existiert. Und derjenige, der den Job hinzufügt, hat ja keine Verpflichtung das Handle bis in alle Ewigkeit zu behalten. Der then-Slot lebt im Thread des Threadpools, nicht im Hauptthread. Ich bräuchte also einen zusätzlichen Mutex ohne shared_ptr.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2506518</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2506518</guid><dc:creator><![CDATA[Eisflamme]]></dc:creator><pubDate>Thu, 25 Aug 2016 12:10:49 GMT</pubDate></item><item><title><![CDATA[Reply to Threadpool mit abbrechbaren Jobs und then-Funktion on Fri, 26 Aug 2016 11:40:37 GMT]]></title><description><![CDATA[<p>Eisflamme schrieb:</p>
<blockquote>
<p>Das eine ersetzt das andere ja nicht.</p>
<p>Denn ohne shared_ptr könnte ich ja keine Handles kopieren. Aber an wessen Scope könnte ich denn dann den bool binden? Wenn der Job bestimmt, wann der atomic&lt;bool&gt; stirbt, kann es sein, dass ich bei einem Abort aus dem Hauptthread ein bool auf false setze, welches nicht mehr existiert. Und derjenige, der den Job hinzufügt, hat ja keine Verpflichtung das Handle bis in alle Ewigkeit zu behalten. Der then-Slot lebt im Thread des Threadpools, nicht im Hauptthread. Ich bräuchte also einen zusätzlichen Mutex ohne shared_ptr.</p>
</blockquote>
<p>wenn du meinst einenen atomic boolean mit einem Mutex beschützen zu müssen, dann OK.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2506661</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2506661</guid><dc:creator><![CDATA[kurze_frage]]></dc:creator><pubDate>Fri, 26 Aug 2016 11:40:37 GMT</pubDate></item><item><title><![CDATA[Reply to Threadpool mit abbrechbaren Jobs und then-Funktion on Fri, 26 Aug 2016 14:04:51 GMT]]></title><description><![CDATA[<p>Hast meinen Post nicht verstanden.</p>
<ul>
<li></li>
</ul>
<p>Falls jemand meint, der Code sei so fehlerfrei, freue ich mich über diese Art von Feedback natürlich ebenfalls.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2506664</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2506664</guid><dc:creator><![CDATA[Eisflamme]]></dc:creator><pubDate>Fri, 26 Aug 2016 14:04:51 GMT</pubDate></item></channel></rss>