0

さまざまな数と型の引数を持つことができるさまざまな関数を実行するために再利用できる C++ でブースト スレッドを作成しようとしています。

これは C++11x 可変引数で実行できますか?

私の使用例では、キューは必要ありません (スレッドがビジーの場合、メソッドは単に失敗します) が、この「統合」機能を実装する必要がある場合は、しぶしぶそうします。

bind または lambda を使用して unify を処理する方法がわかりません。これにより、1 つのスレッドが、それぞれ独自の異なる数量とタイプの引数を持つ異なる関数を呼び出すことができます。

おおむね次のことを念頭に置いています。

class WorkThread
{
public:
   WorkThread()
       {
       // create thread and bind runner to thread
       }

   ~WorkThread()
       {
       // tell runner to exit and wait and reap the thread
       }

   template<typename F,typename ... Arguments>
   void doWork(F func, Arguments... args)
       {
       if already busy
           return false;
       // set indication to runner that there is new work
       // here: how to pass f and args to runner?
       }

private:
   void runner()
        {
        while ( ! time to quit )
            {
            wait for work
            // here: how to get f and args from doWork? do I really need a queue? could wait on a variadic signal maybe?
            f(args);
            }
        }

   boost::thread* m_thread;
};

class ThreadPool
{
public:
    template<typename F, typename ... Arguments>
    bool   doWork(F func,Arguments... args)
          {
          const int i = findAvailableWorkThread();
          m_thread[i].doWork(f,args);
          }
private:
   // a pool of work threads m_thread;
};
4

4 に答える 4

2

これを行う方法を示す既存の質問がたくさんあるはずです。

任意の関数オブジェクトを表す正規の C++11 の方法は ですstd::function<void()>。そのため、ミューテックスで保護する必要がある以下のコードで呼び出されるそのタイプの共有オブジェクトがm_job必要であり、まだ割り当てられていないときに新しいジョブをそれに割り当てます。設定:

template<typename F,typename ... Arguments>
bool doWork(F func, Arguments&&... args)
{
    std::lock_guard<std::mutex> l(m_job_mutex);
    if (m_job)
        return false;
    m_job = std::bind(func, std::forward<Arguments>(args)...);
    m_job_cond.notify_one();  
    return true;   
}

これはstd::bind、関数オブジェクトとその引数を、引数を取らない関数オブジェクトに変換するために使用します。によって返される呼び出し可能なオブジェクトは、各引数std::bindのコピーを格納し、func呼び出されると呼び出しますfunc(args...)

次に、ワーカーは次のことを行います。

void runner()
{
    while ( ! time to quit )
    {
        std::function<void()> job;
        {
            std::unique_lock<std::mutex> l(m_job_mutex);
            while (!m_job)
                m_job_cond.wait(l);
            swap(job, m_job);
        }
        job();
    }
}

このコードはスレッドセーフではありません:

template<typename F, typename ... Arguments>
    bool   doWork(F func,Arguments... args)
    {
        const int i = findAvailableWorkThread();
        m_thread[i].doWork(f,args);
    }

リターン後findAvailableWorkThread、そのスレッドはビジーになる可能性があるため、次の行は失敗します。可用性を確認し、新しいジョブを 1 回の操作で渡す必要があります。

template<typename F, typename ... Arguments>
    bool   doWork(F func,Arguments... args)
    {
        for (auto& t : m_thread)
            if (t.doWork(f,args))
                return true;
        return false;
    }
于 2013-03-17T18:00:38.873 に答える
1

boost::bindまたは C++11を使用して、すべての関数を 1 つのシグネチャに統合できますlambda。したがって、ループ関数を持つスレッドがあり、そのループ内で実行する関数を抽出します。boost::lockfree::queue<boost::function<void()>>それを達成するには、たとえばを使用できます。

アイデアの例として、次のものを使用できます。

class TaskLoop
{
public:
    typedef std::function<void()> Task_t;
public:
    TaskLoop():
        m_IsDone(false)
    {
        m_spThread.reset(new std::thread(&TaskLoop::_run, this));
    }
    ~TaskLoop()
    {
        Task_t task = [this](){m_IsDone = true;};
        postTask(task);
        m_spThread->join();
    }
    void postTask(const Task_t& Msg)
    {
        std::lock_guard<std::mutex> lock(m_Mutex);
        m_Tasks.push(Msg);
    }
    void wait() 
    {
        while(!m_Tasks.empty());
    }
private:
    bool m_IsDone;
    std::unique_ptr<std::thread> m_spThread; 
    std::mutex m_Mutex;
    std::queue<Task_t> m_Tasks;
private:
    void _run()
    {
        while(!m_IsDone)
        {
            Task_t task;
            m_Mutex.lock();
            if(!m_Tasks.empty())
            {
                task = m_Tasks.front();
                m_Tasks.pop();
            }
            m_Mutex.unlock();
            if(task)
                task();
        }
    }
};

void foo(const std::string& first, int second)
{
    std::cout << first << second << "\n";
}

int main(int argc, char **argv)
{
    TaskLoop loop;
    loop.postTask([]{foo("task", 0);});
    loop.wait();
    return 0;
}

例は並行キューを使用せず、非常に単純であるため、キューを置き換えて要件に適合させる必要があります

于 2013-03-06T03:59:08.010 に答える
1

すでにブーストを使用しているため、 Boost.Asio を使用してロックせずに作業関数をキューに入れるhttp://think-async.com/Asio/Recipesのスレッド プール クラスを参照してください。

于 2013-03-17T20:50:42.617 に答える
0

//これは私がこれまでに持っているもので、うまくいくようです....

#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>

#include <queue>
#include <atomic>

class ThreadWorker
    {
    public:
        typedef boost::function<void()> Task_t;
        typedef std::unique_ptr<boost::thread>  UniqueThreadPtr_t;

        ThreadWorker()
            : m_timeToQuit(false)
            {
            m_spThread = UniqueThreadPtr_t( new boost::thread(std::bind(&ThreadWorker::runner, this)));
            }

        virtual ~ThreadWorker()
            {
            quit();
            }

        void quit()
            {
            Task_t task = [this]() { m_timeToQuit = true; };
            enqueue(task);
            m_spThread->join();
            }

        template<typename F,typename ...Args>
        void enqueue(F&& f, Args&&... args)
            {
            boost::mutex::scoped_lock lock(m_jobMutex);
            m_Tasks.push(std::bind(std::forward<F>(f),std::forward<Args>(args)...));
            m_newJob.notify_one();
            }

    private:
        std::atomic<bool>       m_timeToQuit;
        UniqueThreadPtr_t       m_spThread;
        mutable boost::mutex    m_jobMutex;
        std::queue<Task_t>      m_Tasks;
        boost::condition        m_newJob;

    private:
        void runner()
            {
            while ( ! m_timeToQuit )
                {
                Task_t task;
                    {
                    boost::mutex::scoped_lock lock(m_jobMutex);
                    while ( ! task )
                        {
                        if ( m_timeToQuit )
                            {
                            break;
                            }
                        if ( m_Tasks.empty() )
                            {
                            m_newJob.wait(lock);
                            }
                        task = m_Tasks.front();
                        m_Tasks.pop();
                        }
                    }
                if (task)
                    {
                    task();
                    }
                }
            }
    };
于 2013-03-19T17:02:33.237 に答える