Reusing thread in loop c++

前端 未结 6 1030
余生分开走
余生分开走 2020-11-29 02:45

I need to parallelize some tasks in a C++ program and am completely new to parallel programming. I\'ve made some progress through internet searches so far, but am a bit stu

相关标签:
6条回答
  • 2020-11-29 03:22

    This

     std::thread acq1(...)
    

    is the call of an constructor. constructing a new object called acq1

    This

      acq1(...)
    

    is the application of the () operator on the existing object aqc1. If there isn't such a operator defined for std::thread the compiler complains.

    As far as I know you may not reused std::threads. You construct and start them. Join with them and throw them away,

    0 讨论(0)
  • 2020-11-29 03:22

    You also could make your own Thread class and call its run method like:

    class MyThread
    {
    public:
    void run(std::function<void()> func) {
       thread_ = std::thread(func);
    }
    void join() {
       if(thread_.joinable())
          thread_.join();
    }
    private:
       std::thread thread_;
    };
    
    // Application code...
    MyThread myThread;
    myThread.run(AcquireData);
    
    0 讨论(0)
  • The std::thread class is designed to execute exactly one task (the one you give it in the constructor) and then end. If you want to do more work, you'll need a new thread. As of C++11, that's all we have. Thread pools didn't make it into the standard. (I'm uncertain what C++14 has to say about them.)

    Fortunately, you can easily implement the required logic yourself. Here is the large-scale picture:

    • Start n worker threads that all do the following:
      • Repeat while there is more work to do:
        • Grab the next task t (possibly waiting until one becomes ready).
        • Process t.
    • Keep inserting new tasks in the processing queue.
    • Tell the worker threads that there is nothing more to do.
    • Wait for the worker threads to finish.

    The most difficult part here (which is still fairly easy) is properly designing the work queue. Usually, a synchronized linked list (from the STL) will do for this. Synchronized means that any thread that wishes to manipulate the queue must only do so after it has acquired a std::mutex so to avoid race conditions. If a worker thread finds the list empty, it has to wait until there is some work again. You can use a std::condition_variable for this. Each time a new task is inserted into the queue, the inserting thread notifies a thread that waits on the condition variable and will therefore stop blocking and eventually start processing the new task.

    The second not-so-trivial part is how to signal to the worker threads that there is no more work to do. Clearly, you can set some global flag but if a worker is blocked waiting at the queue, it won't realize any time soon. One solution could be to notify_all() threads and have them check the flag each time they are notified. Another option is to insert some distinct “toxic” item into the queue. If a worker encounters this item, it quits itself.

    Representing a queue of tasks is straight-forward using your self-defined task objects or simply lambdas.

    All of the above are C++11 features. If you are stuck with an earlier version, you'll need to resort to third-party libraries that provide multi-threading for your particular platform.

    While none of this is rocket science, it is still easy to get wrong the first time. And unfortunately, concurrency-related bugs are among the most difficult to debug. Starting by spending a few hours reading through the relevant sections of a good book or working through a tutorial can quickly pay off.

    0 讨论(0)
  • 2020-11-29 03:34

    The easiest way is to use a waitable queue of std::function objects. Like this:

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <condition_variable>
    #include <queue>
    #include <functional>
    #include <chrono>
    
    
    class ThreadPool
    {
        public:
    
        ThreadPool (int threads) : shutdown_ (false)
        {
            // Create the specified number of threads
            threads_.reserve (threads);
            for (int i = 0; i < threads; ++i)
                threads_.emplace_back (std::bind (&ThreadPool::threadEntry, this, i));
        }
    
        ~ThreadPool ()
        {
            {
                // Unblock any threads and tell them to stop
                std::unique_lock <std::mutex> l (lock_);
    
                shutdown_ = true;
                condVar_.notify_all();
            }
    
            // Wait for all threads to stop
            std::cerr << "Joining threads" << std::endl;
            for (auto& thread : threads_)
                thread.join();
        }
    
        void doJob (std::function <void (void)> func)
        {
            // Place a job on the queu and unblock a thread
            std::unique_lock <std::mutex> l (lock_);
    
            jobs_.emplace (std::move (func));
            condVar_.notify_one();
        }
    
        protected:
    
        void threadEntry (int i)
        {
            std::function <void (void)> job;
    
            while (1)
            {
                {
                    std::unique_lock <std::mutex> l (lock_);
    
                    while (! shutdown_ && jobs_.empty())
                        condVar_.wait (l);
    
                    if (jobs_.empty ())
                    {
                        // No jobs to do and we are shutting down
                        std::cerr << "Thread " << i << " terminates" << std::endl;
                        return;
                     }
    
                    std::cerr << "Thread " << i << " does a job" << std::endl;
                    job = std::move (jobs_.front ());
                    jobs_.pop();
                }
    
                // Do the job without holding any locks
                job ();
            }
    
        }
    
        std::mutex lock_;
        std::condition_variable condVar_;
        bool shutdown_;
        std::queue <std::function <void (void)>> jobs_;
        std::vector <std::thread> threads_;
    };
    
    void silly (int n)
    {
        // A silly job for demonstration purposes
        std::cerr << "Sleeping for " << n << " seconds" << std::endl;
        std::this_thread::sleep_for (std::chrono::seconds (n));
    }
    
    int main()
    {
        // Create two threads
        ThreadPool p (2);
    
        // Assign them 4 jobs
        p.doJob (std::bind (silly, 1));
        p.doJob (std::bind (silly, 2));
        p.doJob (std::bind (silly, 3));
        p.doJob (std::bind (silly, 4));
    }
    
    0 讨论(0)
  • 2020-11-29 03:39

    Well, it depends if you consider moving a reassigning or not. You can move a thread but not make a copy of it.

    Below code will create new pair of threads each iteration and move them in place of old threads. I imagine this should work, because new thread objects will be temporaries.

    while(user doesn't interrupt)
    {
    //Process first batch of data while acquiring new data
    std::thread proc1(ProcessData,memoryAddress1a);
    std::thread proc2(ProcessData,memoryAddress2a);
    acq1 = std::thread(AcquireData, boardHandle1, memoryAddress1b);
    acq2 = std::thread(AcquireData, boardHandle2, memoryAddress2b);
    acq1.join();
    acq2.join();
    proc1.join();
    proc2.join();
    /*Proceed in this manner, alternating which memory address 
    is written to and being processed until the user interrupts the program.*/
    }
    

    What's going on is, the object actually does not end it's lifetime at the end of the iteration, because it is declared in the outer scope in regard to the loop. But a new object gets created each time and move takes place. I don't see what can be spared (I might be stupid), so I imagine this it's exactly the same as declaring acqs inside the loop and simply reusing the symbol. All in all ... yea, it's about how you classify a create temporary and move.

    Also, this clearly starts a new thread each loop (of course ending the previously assigned thread), it doesn't make a thread wait for new data and magically feed it to the processing pipe. You would need to implement it a differently like. E.g: Worker threads pool and communication over queues.

    References: operator=, (ctor).

    I think the errors you get are self-explanatory, so I'll skip explaining them.

    0 讨论(0)
  • 2020-11-29 03:45

    I think you need a much more simpler answer for running a set of threads more than once, this is the best solution:

    do{
    
        std::vector<std::thread> thread_vector;
    
         for (int i=0;i<nworkers;i++)
         {
           thread_vector.push_back(std::thread(yourFunction,Parameter1,Parameter2, ...));
        }
    
        for(std::thread& it: thread_vector)
        { 
          it.join();
        }
       q++;
    } while(q<NTIMES);
    
    0 讨论(0)
提交回复
热议问题