Joshua Moerman
11 years ago
2 changed files with 106 additions and 0 deletions
@ -0,0 +1,67 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
/* Thread safe queue (with fine grain locks).
|
||||
|
Based on the book by Williams. |
||||
|
No wait_and_pop. |
||||
|
*/ |
||||
|
|
||||
|
#include <memory> |
||||
|
#include <thread> |
||||
|
|
||||
|
template <typename T> |
||||
|
class queue { |
||||
|
private: |
||||
|
struct node { |
||||
|
std::shared_ptr<T> data; |
||||
|
std::unique_ptr<node> next; |
||||
|
}; |
||||
|
|
||||
|
std::mutex head_mutex; |
||||
|
std::unique_ptr<node> head; |
||||
|
|
||||
|
std::mutex tail_mutex; |
||||
|
node* tail; |
||||
|
|
||||
|
node* get_tail(){ |
||||
|
std::lock_guard<std::mutex> lock(tail_mutex); |
||||
|
return tail; |
||||
|
} |
||||
|
|
||||
|
std::unique_ptr<node> pop_head(){ |
||||
|
std::lock_guard<std::mutex> lock(head_mutex); |
||||
|
|
||||
|
if(head.get() == get_tail()){ |
||||
|
return nullptr; |
||||
|
} |
||||
|
|
||||
|
std::unique_ptr<node> old_head = std::move(head); |
||||
|
head = std::move(old_head->next); |
||||
|
return old_head; |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
queue() |
||||
|
: head(new node) |
||||
|
, tail(head.get()) |
||||
|
{} |
||||
|
|
||||
|
queue(const queue &) = delete; |
||||
|
queue& operator=(const queue &) = delete; |
||||
|
|
||||
|
std::shared_ptr<T> try_pop(){ |
||||
|
std::unique_ptr<node> old_head = pop_head(); |
||||
|
return old_head ? old_head->data : nullptr; |
||||
|
} |
||||
|
|
||||
|
void push(T value){ |
||||
|
auto new_data = std::make_shared<T>(std::move(value)); |
||||
|
std::unique_ptr<node> p(new node); |
||||
|
|
||||
|
node* const new_tail = p.get(); |
||||
|
std::lock_guard<std::mutex> lock(tail_mutex); |
||||
|
|
||||
|
tail->data = new_data; |
||||
|
tail->next = std::move(p); |
||||
|
tail = new_tail; |
||||
|
} |
||||
|
}; |
@ -0,0 +1,39 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
/* Simple thread pool, no fancy stuff.
|
||||
|
It is thread safe (i.e. concurrent adds). |
||||
|
Threads terminate if there is no work (we need wait_and_pop for that). |
||||
|
Works best if all work is added, and then run. |
||||
|
Doesn't care about exceptions. |
||||
|
*/ |
||||
|
|
||||
|
#include <vector> |
||||
|
#include <functional> |
||||
|
#include <thread> |
||||
|
#include "lock_queue.hpp" |
||||
|
|
||||
|
struct thread_pool { |
||||
|
void add(std::function<void()> f){ |
||||
|
work.push(f); |
||||
|
} |
||||
|
|
||||
|
void run(int number_of_threads){ |
||||
|
for(int i = 0; i < number_of_threads; ++i){ |
||||
|
threads.emplace_back([this]{ |
||||
|
auto x = work.try_pop(); |
||||
|
while(x){ |
||||
|
(*x)(); |
||||
|
x = work.try_pop(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
~thread_pool(){ |
||||
|
for(auto & t : threads) t.join(); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
queue<std::function<void()>> work; |
||||
|
std::vector<std::thread> threads; |
||||
|
}; |
Reference in new issue