Extend the life of threads with synchronization (C++11)

后端 未结 4 1352
不知归路
不知归路 2020-12-08 03:17

I have a program with a function which takes a pointer as arg, and a main. The main is creating n threads, each of them running the function on different me

4条回答
  •  广开言路
    2020-12-08 03:59

    Here is a possible approach using only classes from the C++11 Standard Library. Basically, each thread you create has an associated command queue (encapsulated in std::packaged_task<> objects) which it continuously check. If the queue is empty, the thread will just wait on a condition variable (std::condition_variable).

    While data races are avoided through the use of std::mutex and std::unique_lock<> RAII wrappers, the main thread can wait for a particular job to be terminated by storing the std::future<> object associated to each submitted std::packaged_tast<> and call wait() on it.

    Below is a simple program that follows this design. Comments should be sufficient to explain what it does:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    // Convenience type definition
    using job = std::packaged_task;
    
    // Some data associated to each thread.
    struct thread_data
    {
        int id; // Could use thread::id, but this is filled before the thread is started
        std::thread t; // The thread object
        std::queue jobs; // The job queue
        std::condition_variable cv; // The condition variable to wait for threads
        std::mutex m; // Mutex used for avoiding data races
        bool stop = false; // When set, this flag tells the thread that it should exit
    };
    
    // The thread function executed by each thread
    void thread_func(thread_data* pData)
    {
        std::unique_lock l(pData->m, std::defer_lock);
        while (true)
        {
            l.lock();
    
            // Wait until the queue won't be empty or stop is signaled
            pData->cv.wait(l, [pData] () {
                return (pData->stop || !pData->jobs.empty()); 
                });
    
            // Stop was signaled, let's exit the thread
            if (pData->stop) { return; }
    
            // Pop one task from the queue...
            job j = std::move(pData->jobs.front());
            pData->jobs.pop();
    
            l.unlock();
    
            // Execute the task!
            j();
        }
    }
    
    // Function that creates a simple task
    job create_task(int id, int jobNumber)
    {
        job j([id, jobNumber] ()
        {
            std::stringstream s;
            s << "Hello " << id << "." << jobNumber << std::endl;
            std::cout << s.str();
        });
    
        return j;
    }
    
    int main()
    {
        const int numThreads = 4;
        const int numJobsPerThread = 10;
        std::vector> futures;
    
        // Create all the threads (will be waiting for jobs)
        thread_data threads[numThreads];
        int tdi = 0;
        for (auto& td : threads)
        {
            td.id = tdi++;
            td.t = std::thread(thread_func, &td);
        }
    
        //=================================================
        // Start assigning jobs to each thread...
    
        for (auto& td : threads)
        {
            for (int i = 0; i < numJobsPerThread; i++)
            {
                job j = create_task(td.id, i);
                futures.push_back(j.get_future());
    
                std::unique_lock l(td.m);
                td.jobs.push(std::move(j));
            }
    
            // Notify the thread that there is work do to...
            td.cv.notify_one();
        }
    
        // Wait for all the tasks to be completed...
        for (auto& f : futures) { f.wait(); }
        futures.clear();
    
    
        //=================================================
        // Here the main thread does something...
    
        std::cin.get();
    
        // ...done!
        //=================================================
    
    
        //=================================================
        // Posts some new tasks...
    
        for (auto& td : threads)
        {
            for (int i = 0; i < numJobsPerThread; i++)
            {
                job j = create_task(td.id, i);
                futures.push_back(j.get_future());
    
                std::unique_lock l(td.m);
                td.jobs.push(std::move(j));
            }
    
            // Notify the thread that there is work do to...
            td.cv.notify_one();
        }
    
        // Wait for all the tasks to be completed...
        for (auto& f : futures) { f.wait(); }
        futures.clear();
    
        // Send stop signal to all threads and join them...
        for (auto& td : threads)
        {
            std::unique_lock l(td.m);
            td.stop = true;
            td.cv.notify_one();
        }
    
        // Join all the threads
        for (auto& td : threads) { td.t.join(); }
    }
    

提交回复
热议问题