I am trying to set the name of my threads for ease of profiling (in ps, top, etc.).
I usually use pthread_setname_np(pthread_self(), <THREAD_NAME>) and it works like a charm.
But the one place I cannot understand is how to do it when I'm using lambda threads.
Here's an example on what I'm trying to do.
#include <iostream>
#include <thread>
#include <vector>
#include <algorithm>
#include <pthread.h>
int main()
{
// vector container stores threads
std::vector<std::thread> workers;
for (int i = 0; i < 5; i++) {
workers.push_back(std::thread([]()
{
std::cout << "thread function\n";
}));
}
std::cout << "main thread\n";
std::for_each(workers.begin(), workers.end(), [](std::thread& t)
{
//I want to set the thread name for profiling
std::string s = "mythread";
auto handle = t.native_handle();
pthread_setname_np(handle, s.c_str());
t.join();
});
return 0;
}
This does not work. If I do top -H -p <pid> in Linux I cannot see those threads named.
Thanks
Related
I have the program to count all words in all .log files in given directory using N threads.
I wrote something like this.
ThreadPool.h
#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include <boost/thread/condition_variable.hpp>
#include <boost/thread.hpp>
#include <future> // I don't how to work with boost future
#include <queue>
#include <vector>
#include <functional>
class ThreadPool
{
public:
using Task = std::function<void()>; // Our task
explicit ThreadPool(int num_threads)
{
start(num_threads);
}
~ThreadPool()
{
stop();
}
template<class T>
auto enqueue(T task)->std::future<decltype(task())>
{
// packaged_task wraps any Callable target
auto wrapper = std::make_shared<std::packaged_task<decltype(task()) ()>>(std::move(task));
{
boost::unique_lock<boost::mutex> lock{ mutex_p };
tasks_p.emplace([=] {
(*wrapper)();
});
}
event_p.notify_one();
return wrapper->get_future();
}
/*void enqueue(Task task)
{
{
boost::unique_lock<boost::mutex> lock { mutex_p };
tasks_p.emplace(std::move(task));
event_p.notify_one();
}
}*/
private:
std::vector<boost::thread> threads_p; // num of threads
std::queue<Task> tasks_p; // Tasks to make
boost::condition_variable event_p;
boost::mutex mutex_p;
bool isStop = false;
void start(int num_threads)
{
for (int i = 0; i < num_threads; ++i)
{
// Add to the end our thread
threads_p.emplace_back([=] {
while (true)
{
// Task to do
Task task;
{
boost::unique_lock<boost::mutex> lock(mutex_p);
event_p.wait(lock, [=] { return isStop || !tasks_p.empty(); });
// If we make all tasks
if (isStop && tasks_p.empty())
break;
// Take new task from queue
task = std::move(tasks_p.front());
tasks_p.pop();
}
// Execute our task
task();
}
});
}
}
void stop() noexcept
{
{
boost::unique_lock<boost::mutex> lock(mutex_p);
isStop = true;
}
event_p.notify_all();
for (auto& thread : threads_p)
{
thread.join();
}
}
};
#endif
main.cpp
#include "ThreadPool.h"
#include <iostream>
#include <iomanip>
#include <Windows.h>
#include <chrono>
#include <vector>
#include <map>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <locale.h>
namespace bfs = boost::filesystem;
//int count_words(boost::filesystem::ifstream& file)
//{
// int counter = 0;
// std::string buffer;
// while (file >> buffer)
// {
// ++counter;
// }
//
// return counter;
//}
//
int count_words(boost::filesystem::path filename)
{
boost::filesystem::ifstream ifs(filename);
return std::distance(std::istream_iterator<std::string>(ifs), std::istream_iterator<std::string>());
}
int main(int argc, const char* argv[])
{
std::cin.tie(0);
std::ios_base::sync_with_stdio(false);
bfs::path path = argv[1];
// If this path is exist and if this is dir
if (bfs::exists(path) && bfs::is_directory(path))
{
// Number of threads. Default = 4
int n = (argc == 3 ? atoi(argv[2]) : 4);
ThreadPool pool(n);
// Container to store all filenames and number of words inside them
//std::map<bfs::path, std::future<int>> all_files_and_sums;
std::vector<std::future<int>> futures;
auto start = std::chrono::high_resolution_clock::now();
// Iterate all files in dir
for (auto& p : bfs::directory_iterator(path)) {
// Takes only .txt files
if (p.path().extension() == ".log") {
// Future for taking value from here
auto fut = pool.enqueue([p]() {
// In this lambda function I count all words in file and return this value
int result = count_words(p.path());
static int count = 0;
++count;
std::ostringstream oss;
oss << count << ". TID, " << GetCurrentThreadId() << "\n";
std::cout << oss.str();
return result;
});
// "filename = words in this .txt file"
futures.emplace_back(std::move(fut));
}
}
int result = 0;
for (auto& f : futures)
{
result += f.get();
}
auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::seconds>(stop - start);
std::cout << "Result: " << result << "\n";
std::cout << duration.count() << '\n';
}
else
std::perror("Dir is not exist");
}
Variable N is 4(Number of threads). I've 320 .log files in my directory and I need count words in this files. Everything works fine but when variable "count" is 180 - the program stops for a while and then continues but much slower.
What could be the reason?
CPU - Xeon e5430 (I have tested this program on another CPU - the result is the same).
It depends on how you measure "slow" but basically you are using one of the worst models possible:
one task queue shared between all threads.
The problem with this approach is blocking in each thread on the shared queue.
A much better model is something like
task stealing - you can try creating a task queue pro thread and then use try_lock (which doesnt block) with enabling each thread "stealing" work from some other thread's tasks if it has nothing else to do.
This is very nice explained in excellent Sean Parent Talk about Concurrency.
I've implemented thread pooling following the answer of Kerrek SB in this question.
I've implemented MPMC queue for the functions and vector threads for the threads.
Everything worked perfectly, except that I don't know how to terminate the program, in the end if I just do thread.join since the thread is still waiting for more tasks to do, it will not join and the main thread will not continue.
Any idea how to end the program correctly?
For completeness, this is my code:
function_pool.h
#pragma once
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
class Function_pool
{
private:
std::queue<std::function<void()>> m_function_queue;
std::mutex m_lock;
std::condition_variable m_data_condition;
public:
Function_pool();
~Function_pool();
void push(std::function<void()> func);
std::function<void()> pop();
};
function_pool.cpp
#include "function_pool.h"
Function_pool::Function_pool() : m_function_queue(), m_lock(), m_data_condition()
{
}
Function_pool::~Function_pool()
{
}
void Function_pool::push(std::function<void()> func)
{
std::unique_lock<std::mutex> lock(m_lock);
m_function_queue.push(func);
// when we send the notification immediately, the consumer will try to
get the lock , so unlock asap
lock.unlock();
m_data_condition.notify_one();
}
std::function<void()> Function_pool::pop()
{
std::unique_lock<std::mutex> lock(m_lock);
m_data_condition.wait(lock, [this]() {return !m_function_queue.empty();
});
auto func = m_function_queue.front();
m_function_queue.pop();
return func;
// Lock will be released
}
main.cpp
#include "function_pool.h"
#include <string>
#include <iostream>
#include <mutex>
#include <functional>
#include <thread>
#include <vector>
Function_pool func_pool;
void example_function()
{
std::cout << "bla" << std::endl;
}
void infinite_loop_func()
{
while (true)
{
std::function<void()> func = func_pool.pop();
func();
}
}
int main()
{
std::cout << "stating operation" << std::endl;
int num_threads = std::thread::hardware_concurrency();
std::cout << "number of threads = " << num_threads << std::endl;
std::vector<std::thread> thread_pool;
for (int i = 0; i < num_threads; i++)
{
thread_pool.push_back(std::thread(infinite_loop_func));
}
//here we should send our functions
func_pool.push(example_function);
for (int i = 0; i < thread_pool.size(); i++)
{
thread_pool.at(i).join();
}
int i;
std::cin >> i;
}
Your problem is located in infinite_loop_func, which is an infinite loop and by result doesn't terminate. I've read the previous answer which suggests throwing an exception, however, I don't like it since exceptions should not be used for the regular control flow.
The best way to solve this is to explicitly deal with the stop condition. For example:
std::atomic<bool> acceptsFunctions;
Adding this to the function pool allows you to clearly have state and to assert that no new functions being added when you destruct.
std::optional<std::function<void()>> Function_pool::pop()
Returning an empty optional (or function in C++14 and before), allows you to deal with an empty queue. You have to, as condition_variable can do spurious wakeups.
With this, m_data_condition.notify_all() can be used to wake all threads.
Finally we have to fix the infinite loop as it doesn't cover overcommitment and at the same time allows you to execute all functions still in the queue:
while (func_pool.acceptsFunctions || func_pool.containsFunctions())
{
auto f = func_pool.pop();
If (!f)
{
func_pool.m_data_condition.wait_for(1s);
continue;
}
auto &function = *f;
function ();
}
I'll leave it up to you to implement containsFunctions() and clean up the code (infinite_loop_func as member function?) Note that with a counter, you could even deal with background task being spawned.
You can always use a specific exception type to signal to infinite_loop_func that it should return...
class quit_worker_exception: public std::exception {};
Then change infinite_loop_func to...
void infinite_loop_func ()
{
while (true) {
std::function<void()> func = func_pool.pop();
try {
func();
}
catch (quit_worker_exception &ex) {
return;
}
}
}
With the above changes you could then use (in main)...
/*
* Enqueue `thread_pool.size()' function objects whose sole job is
* to throw an instance of `quit_worker_exception' when invoked.
*/
for (int i = 0; i < thread_pool.size(); i++)
func_pool.push([](){ throw quit_worker_exception(); });
/*
* Now just wait for each worker to terminate having received its
* quit_worker_exception.
*/
for (int i = 0; i < thread_pool.size(); i++)
thread_pool.at(i).join();
Each instance of infinite_loop_func will dequeue one function object which, when called, throws a quit_worker_exception causing it to return.
Follwoing [JVApen](https://stackoverflow.com/posts/51382714/revisions) suggestion, I copy my code in case anyone will want a working code:
function_pool.h
#pragma once
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <cassert>
class Function_pool
{
private:
std::queue<std::function<void()>> m_function_queue;
std::mutex m_lock;
std::condition_variable m_data_condition;
std::atomic<bool> m_accept_functions;
public:
Function_pool();
~Function_pool();
void push(std::function<void()> func);
void done();
void infinite_loop_func();
};
function_pool.cpp
#include "function_pool.h"
Function_pool::Function_pool() : m_function_queue(), m_lock(), m_data_condition(), m_accept_functions(true)
{
}
Function_pool::~Function_pool()
{
}
void Function_pool::push(std::function<void()> func)
{
std::unique_lock<std::mutex> lock(m_lock);
m_function_queue.push(func);
// when we send the notification immediately, the consumer will try to get the lock , so unlock asap
lock.unlock();
m_data_condition.notify_one();
}
void Function_pool::done()
{
std::unique_lock<std::mutex> lock(m_lock);
m_accept_functions = false;
lock.unlock();
// when we send the notification immediately, the consumer will try to get the lock , so unlock asap
m_data_condition.notify_all();
//notify all waiting threads.
}
void Function_pool::infinite_loop_func()
{
std::function<void()> func;
while (true)
{
{
std::unique_lock<std::mutex> lock(m_lock);
m_data_condition.wait(lock, [this]() {return !m_function_queue.empty() || !m_accept_functions; });
if (!m_accept_functions && m_function_queue.empty())
{
//lock will be release automatically.
//finish the thread loop and let it join in the main thread.
return;
}
func = m_function_queue.front();
m_function_queue.pop();
//release the lock
}
func();
}
}
main.cpp
#include "function_pool.h"
#include <string>
#include <iostream>
#include <mutex>
#include <functional>
#include <thread>
#include <vector>
Function_pool func_pool;
class quit_worker_exception : public std::exception {};
void example_function()
{
std::cout << "bla" << std::endl;
}
int main()
{
std::cout << "stating operation" << std::endl;
int num_threads = std::thread::hardware_concurrency();
std::cout << "number of threads = " << num_threads << std::endl;
std::vector<std::thread> thread_pool;
for (int i = 0; i < num_threads; i++)
{
thread_pool.push_back(std::thread(&Function_pool::infinite_loop_func, &func_pool));
}
//here we should send our functions
for (int i = 0; i < 50; i++)
{
func_pool.push(example_function);
}
func_pool.done();
for (unsigned int i = 0; i < thread_pool.size(); i++)
{
thread_pool.at(i).join();
}
}
I have a program which takes several input from the user and based on the input it posts the work to a thread pool like the following:
while (getline (file, input)){
if(input =='a'){
ioService_.post(boost::bind(&myClass::myFunction, this, input, counter));
} //end if
else if(input=='b'){
ioService_.post(boost::bind(importedFunction, input));
}
else{
break;
}
}//end while
threadpool.join_all();
}
The function importedFunction is imported from another class and the code works correctly with it but for the myFunction function it does not post work.
I was originally binding the function as this:
ioService_.post(boost::bind(myClass::myFunction, input, counter));
and it was giving me an error which is:
/usr/local/include/boost/bind/bind.hpp:75:22: Type 'void
(multithreading::*)(std::__1::basic_string, int)' cannot be used
prior to '::' because it has no members
and then I changed into this and solved the error:
ioService_.post(boost::bind(&myClass::myFunction, this, input, counter));
but now the work is not submitted and I don't know why.
I suspect you may have forgotten to run() the io_service.
If so, add
ioService_.run();
somehwere.
And perhaps read up about io_service::work to keep the service alive between tasks. It is possible that service threads completed the run() before you posted the work.
I have faced the same issue. It is because of the io_service::work. The task this Copy constructor notifies the io_service that work is starting.
Here is a working example, if someone else needs it.
#include <iostream>
#include <unistd.h>
#include <iostream>
#include <thread>
#include <vector>
#include <algorithm>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/move/move.hpp>
#include <boost/make_unique.hpp>
namespace asio = boost::asio;
typedef boost::packaged_task<int> task_t;
typedef boost::shared_ptr<task_t> ptask_t;
class Task
{
public:
int execute(std::string command)
{
//TODO actual logic
std::cout<< "\nThread:" << command << std::endl;
int sum = 0;
for(int i = 0; i < 5; i++)
{
sum+=i;
}
return sum;
}
};
void push_job(Task* worker, std::string seconds, boost::asio::io_service& io_service
, std::vector<boost::shared_future<int> >& pending_data) {
ptask_t task = boost::make_shared<task_t>(boost::bind(&Task::execute, worker, seconds));
boost::shared_future<int> fut(task->get_future());
pending_data.push_back(fut);
io_service.post(boost::bind(&task_t::operator(), task));
}
int main()
{
Task* taskPtr = new Task();
boost::asio::io_service io_service;
boost::thread_group threads;
std::unique_ptr<boost::asio::io_service::work> service_work;
service_work = boost::make_unique<boost::asio::io_service::work>(io_service);
for (int i = 0; i < boost::thread::hardware_concurrency() ; ++i)
{
threads.create_thread(boost::bind(&boost::asio::io_service::run,
&io_service));
}
std::vector<boost::shared_future<int> > pending_data; // vector of futures
push_job(taskPtr, "4", io_service, pending_data);
push_job(taskPtr, "5", io_service, pending_data);
push_job(taskPtr, "6", io_service, pending_data);
push_job(taskPtr, "7", io_service, pending_data);
boost::wait_for_all(pending_data.begin(), pending_data.end());
int total_sum = 0;
for(auto result : pending_data){
total_sum += result.get();
}
std::cout<< "Total sum: "<< total_sum << std::endl;
return 0;
}
I`ve made a test code between std::thread and std::async.
#include <iostream>
#include <mutex>
#include <fstream>
#include <string>
#include <memory>
#include <thread>
#include <future>
#include <functional>
#include <boost/noncopyable.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio.hpp>
namespace fs = boost::filesystem;
namespace pt = boost::posix_time;
namespace as = boost::asio;
class Log : private boost::noncopyable
{
public:
void LogPath(const fs::path& filePath) {
boost::system::error_code ec;
if(fs::exists(filePath, ec)) {
fs::remove(filePath);
}
this->ofStreamPtr_.reset(new fs::ofstream(filePath));
};
void WriteLog(std::size_t i) {
assert(*this->ofStreamPtr_);
std::lock_guard<std::mutex> lock(this->logMutex_);
*this->ofStreamPtr_ << "Hello, World! " << i << "\n";
};
private:
std::mutex logMutex_;
std::unique_ptr<fs::ofstream> ofStreamPtr_;
};
int main(int argc, char *argv[]) {
if(argc != 2) {
std::cout << "Wrong argument" << std::endl;
exit(1);
}
std::size_t iter_count = boost::lexical_cast<std::size_t>(argv[1]);
Log log;
log.LogPath("log.txt");
std::function<void(std::size_t)> func = std::bind(&Log::WriteLog, &log, std::placeholders::_1);
auto start_time = pt::microsec_clock::local_time();
////// Version 1: use std::thread //////
// {
// std::vector<std::shared_ptr<std::thread> > threadList;
// threadList.reserve(iter_count);
// for(std::size_t i = 0; i < iter_count; i++) {
// threadList.push_back(
// std::make_shared<std::thread>(func, i));
// }
//
// for(auto it: threadList) {
// it->join();
// }
// }
// pt::time_duration duration = pt::microsec_clock::local_time() - start_time;
// std::cout << "Version 1: " << duration << std::endl;
////// Version 2: use std::async //////
start_time = pt::microsec_clock::local_time();
{
for(std::size_t i = 0; i < iter_count; i++) {
auto result = std::async(func, i);
}
}
duration = pt::microsec_clock::local_time() - start_time;
std::cout << "Version 2: " << duration << std::endl;
////// Version 3: use boost::asio::io_service //////
// start_time = pt::microsec_clock::local_time();
// {
// as::io_service ioService;
// as::io_service::strand strand{ioService};
// {
// for(std::size_t i = 0; i < iter_count; i++) {
// strand.post(std::bind(func, i));
// }
// }
// ioService.run();
// }
// duration = pt::microsec_clock::local_time() - start_time;
// std::cout << "Version 3: " << duration << std::endl;
}
With 4-core CentOS 7 box(gcc 4.8.5), Version 1(using std::thread) is about 100x slower compared to other implementations.
Iteration Version1 Version2 Version3
100 0.0034s 0.000051s 0.000066s
1000 0.038s 0.00029s 0.00058s
10000 0.41s 0.0042s 0.0059s
100000 throw 0.026s 0.061s
Why threaded version is so slow? I thought each thread won't take long time to complete Log::WriteLog function.
The function may never be called. You are not passing an std::launch policy in Version 2, so you are relying on the default behavior of std::async (emphasis mine):
Behaves the same as async(std::launch::async | std::launch::deferred, f, args...). In other words, f may be executed in another thread or it may be run synchronously when the resulting std::future is queried for a value.
Try re-running your benchmark with this minor change:
auto result = std::async(std::launch::async, func, i);
Alternatively, you could call result.wait() on each std::future in a second loop, similar to how you call join() on all of the threads in Version 1. This forces evaluation of the std::future.
Note that there is a major, unrelated, problem with this benchmark. func immediately acquires a lock for the full duration of the function call, which makes parallelism impossible. There is no advantage to using threads here - I suspect that it will be significantly slower (due to thread creation and locking overhead) than a serial implementation.
Here is my simple code, I want to get in the console_task, the value of the variable i in the dialer_task... without using a global variable.
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <thread>
#include "console.hpp"
using namespace std;
void console_task(){
console();
}
void dialer_task(){
int i=0;
while (1) {
printf("LOOP %d\n",i);
i++;
sleep(5);
}
}
int main()
{
thread t1(console_task);
thread t2(dialer_task);
t1.join();
t2.join();
return 0;
}
The constraint that there may not be a global variable to share the state between the threads leaves essentially 2 viable alternatives;
Allocate the shared state on the heap and pass that on to the threads
Allocate the shared state on the original thread's stack and "feed" it to the worker threads for shared use.
The catch to both solutions is to make sure that the access is appropriately guarded or atomic.
A simple solution is to use an std::atomic and share the reference between the threads.
#include <type_traits>
#include <thread>
#include <atomic>
#include <iostream>
void console_task(std::atomic_int& j) {
using namespace std;
int i = 0;
while (++i < 50) {
cout << "task " << j << endl; // uncontrolled access to the console (demo)
std::chrono::microseconds delay{50};
this_thread::sleep_for(delay);
}
}
void dialer_task(std::atomic_int& j){
using namespace std;
int i = 0;
while ( ++i < 10) {
//cout << "LOOP " << i << endl; // uncontrolled access to the console (demo)
std::chrono::microseconds delay{145};
this_thread::sleep_for(delay);
j = i;
}
}
int main()
{
std::atomic_int i { 0 };
std::thread t1( console_task, std::ref(i));
// a lambda with reference capture could also be used
// std::thread t1( [&](){console_task(i);} );
std::thread t2( dialer_task, std::ref(i));
t1.join();
t2.join();
return 0;
}
There is a catch to the shared atomic, it needs to remain valid for the duration of the threads (as it does here).
Demo code.
Further heap based alternatives can be considered; e.g. using a shared std::mutex together with a std::shared_ptr.