-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add an alternate method to enqueueAndDetach a task without requiring the capture of std::future<> #31
Open
Calthron
wants to merge
10
commits into
progschj:master
Choose a base branch
from
Calthron:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add an alternate method to enqueueAndDetach a task without requiring the capture of std::future<> #31
Changes from 8 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
1eb6812
Add ability to enqueue a task that does not require monitoring a retu…
Calthron a37e613
Typo in comment
Calthron e463df9
Break into header and implementation
Calthron 16e0dbf
Rename ThreadPool.h to ThreadPool.hpp
Calthron 78b440b
Comment update
Calthron 9db68cc
Use typename instead of class
Calthron 834fc0c
Use break instead of return
Calthron f761d52
Set default thread pool size to 1
Calthron 3a5d8a8
Use lock_t instead of Lock
Calthron cbb46e0
Remove enqueAndDetach
Calthron File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Purpose: Thread pool | ||
|
||
// Based on https://github.com/progschj/ThreadPool | ||
|
||
#include "ThreadPool.hpp" | ||
|
||
ThreadPool::ThreadPool (size_t threads) | ||
{ | ||
workers.reserve (threads); | ||
|
||
for (size_t count = 0; count < threads; ++count) | ||
{ | ||
// Worker execution loop | ||
workers.emplace_back ([this]() | ||
{ | ||
for (;;) | ||
{ | ||
// Task to execute | ||
std::function<void ()> task; | ||
|
||
// Wait for additional work signal | ||
{ // Critical section | ||
// Wait to be notified of work | ||
Lock lock (this->queue_mutex); | ||
this->condition.wait (lock, [this]() | ||
{ | ||
return this->stop || !this->tasks.empty (); | ||
}); | ||
|
||
// If stopping and no work remains, exit the work loop and thread | ||
if (this->stop && this->tasks.empty ()) | ||
break; | ||
|
||
// Dequeue the next task | ||
task = std::move (this->tasks.front ()); | ||
this->tasks.pop (); | ||
} // End critical section | ||
|
||
// Execute | ||
task (); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
// Destructor joins all worker threads | ||
ThreadPool::~ThreadPool () | ||
{ | ||
{ // Critical section | ||
Lock lock (queue_mutex); | ||
stop = true; | ||
} // End critical section | ||
|
||
condition.notify_all (); | ||
|
||
// Wait for threads to complete work | ||
for (std::thread &worker : workers) | ||
{ | ||
worker.join(); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Purpose: Thread pool | ||
|
||
// Based on https://github.com/progschj/ThreadPool | ||
|
||
#pragma once | ||
|
||
#include <vector> | ||
#include <queue> | ||
#include <memory> | ||
#include <thread> | ||
#include <mutex> | ||
#include <condition_variable> | ||
#include <future> | ||
#include <functional> | ||
#include <stdexcept> | ||
|
||
class ThreadPool final | ||
{ | ||
public: | ||
// Launches specified number of worker threads | ||
ThreadPool (size_t threads = 1); | ||
~ThreadPool (); | ||
|
||
// Not copyable | ||
ThreadPool (const ThreadPool &) = delete; | ||
ThreadPool& operator= (const ThreadPool &) = delete; | ||
|
||
// Not moveable | ||
ThreadPool (ThreadPool &&) = delete; | ||
ThreadPool& operator= (const ThreadPool &&) = delete; | ||
|
||
// Enqueue task and return std::future<> | ||
template<typename Callable, typename... Args> | ||
auto enqueue (Callable&& callable, Args&&... args) | ||
-> std::future<typename std::result_of<Callable (Args...)>::type>; | ||
|
||
// Enqueue task without requiring capture of std::future<> | ||
// Note: Best not to let exceptions escape the callable | ||
template<typename Callable, typename... Args> | ||
auto enqueueAndDetach (Callable&& callable, Args&&... args) | ||
-> void; | ||
|
||
private: | ||
// Keep track of threads, so they can be joined | ||
std::vector<std::thread> workers; | ||
// Task queue | ||
std::queue<std::function<void ()>> tasks; | ||
|
||
// Synchronization | ||
using Lock = std::unique_lock<std::mutex>; | ||
std::mutex queue_mutex; | ||
std::condition_variable condition; | ||
bool stop = false; | ||
}; | ||
|
||
// Add a new work item to the pool | ||
template<typename Callable, typename... Args> | ||
auto ThreadPool::enqueue (Callable&& callable, Args&&... args) | ||
-> std::future<typename std::result_of<Callable (Args...)>::type> | ||
{ | ||
using return_t = typename std::result_of<Callable (Args...)>::type; | ||
using task_t = std::packaged_task<return_t ()>; | ||
|
||
auto task = std::make_shared<task_t> (std::bind (std::forward<Callable> (callable), std::forward<Args> (args)...)); | ||
std::future<return_t> result = task->get_future(); | ||
|
||
{ // Critical section | ||
Lock lock (queue_mutex); | ||
|
||
// Don't allow an enqueue after stopping | ||
if (stop) | ||
throw std::runtime_error ("enqueue on stopped ThreadPool"); | ||
|
||
// Push work back on the queue | ||
tasks.emplace ([task](){ (*task)(); }); | ||
} // End critical section | ||
|
||
// Notify a thread that there is new work to perform | ||
condition.notify_one (); | ||
return result; | ||
} | ||
|
||
// Add a new work item to the pool | ||
template<typename Callable, typename... Args> | ||
auto ThreadPool::enqueueAndDetach (Callable&& callable, Args&&... args) | ||
-> void | ||
{ | ||
{ // Critical section | ||
Lock lock (queue_mutex); | ||
|
||
// Don't allow an enqueue after stopping | ||
if (stop) | ||
throw std::runtime_error ("enqueue on stopped ThreadPool"); | ||
|
||
// Push work back on the queue | ||
tasks.emplace (std::bind (std::forward<Callable> (callable), std::forward<Args> (args)...)); | ||
} // End critical section | ||
|
||
// Notify a thread that there is new work to perform | ||
condition.notify_one (); | ||
} | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@progschj isn't this going to cause a throw from destructor potentially? that seems to the be only time when "stop" is called. IIRC you can never cleanly catch such an exception -- DTOR throw is undefined behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not sure I understand the question.
This would be throwing on a caller’s thread when the internal ‘stop’ flag is true, and the caller was trying to add a task. If the caller is calling member functions while the object is being destroyed… that would be another sort of issue.
From: peter karasev [mailto:[email protected]]
Sent: Thursday, June 16, 2016 11:56 AM
To: progschj/ThreadPool [email protected]
Cc: Steven LeMay [email protected]; Author [email protected]
Subject: Re: [progschj/ThreadPool] Add an alternate method to enqueueAndDetach a task without requiring the capture of std::future<> (#31)
In ThreadPool.hpp #31 (comment) :
@progschj https://github.com/progschj isn't this going to cause a throw from destructor potentially? that seems to the be only time when "stop" is called. IIRC you can never cleanly catch such an exception -- DTOR throw is undefined behavior.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub https://github.com/progschj/ThreadPool/pull/31/files/f761d527a83a37003ecef53c94718818e60d8601#r67404012 , or mute the thread https://github.com/notifications/unsubscribe/AKJadATwrIy1WhowY4pCzd32mUBhU6JSks5qMZxagaJpZM4GfmQ6 . https://github.com/notifications/beacon/AKJadA15Aixeyyd70laKpBGr_6yKx0KRks5qMZxagaJpZM4GfmQ6.gif