2017-02-04 49 views
0

我已經使用std::sthread來加載背景圖片。我創建後臺作業,因爲這:用於後臺加載的C++線程

if (bgThreads.size() > MAX_THREADS_COUNT){ 
    fclose(file); 
    return; 
} 

if (bgThreads.find(id) != bgThreads.end()){ 
    fclose(file); 
    return; 
} 
std::shared_ptr<BackgroundPNGLoader> bg = std::make_shared<BackgroundPNGLoader>(file, id); 
bgThreads[id] = bg; 
bg->thread = std::thread(&BackgroundPNGLoader::Run, bg);  

在BackgroundPNGLoader,我有:

struct BackgroundPNGLoader{ 
    std::atomic<bool> finished; 
    FILE * f; 
    int id; 
    BackgroundPNGLoader() : finished(false) {} 

    void Run(){ 
     ///.... load data 
     finished.store(true); 
    } 
} 

在我的主要應用程序,我有更新 - 渲染環路主線程中運行。在更新中,我有:

std::list<int> finished; 
for (auto & it : bgThreads) 
{ 
    if (it.second->finished) 
    { 
     if (it.second->thread.joinable()) 
     { 
      it.second->thread.join(); 
     } 

     finished.push_back(it.first); 
     //fill image data to texture or whatever need to be done on main thread 
     fclose(it.second->f); 
    } 
} 

for (auto & it : finished) 
{ 
    bgThreads.erase(it); 
} 

這是否認爲安全?每次我需要打開新文件時,我都有點擔心產生新線程,但沒有最大限制。

回答

1

首先,避免fopen/fclose並使用C++的文件I/O來避免拋出異常時潛在的資源泄漏。此外,在大多數情況下,使用原始的std :: thread不是必需的。對於異步任務,std :: async加上期貨是合適的。 的std ::異步返回的std ::未來,以後可以檢索內可能的結果:

#include <future> 
#include <thread> 
#include <chrono> 
#include <array> 
#include <iostream> 
#include <random> 

size_t doHeavyWork(size_t arg) 
{ 
    std::mt19937 rng; 
    rng.seed(std::random_device()()); 
    std::uniform_int_distribution<unsigned int> rnd(333, 777); 
    //simulate heavy work... 
    std::this_thread::sleep_for(std::chrono::milliseconds(rnd(rng))); 
    return arg * 33; 
} 

//wrapper function for checking whether a task has finished and 
//the result can be retrieved by a std::future 
template<typename R> 
bool isReady(const std::future<R> &f) 
{ 
    return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready; 
} 

int main() 
{ 
    constexpr size_t numTasks = 5; 

    std::array<std::future<size_t>, numTasks> futures; 

    size_t index = 1; 
    for(auto &f : futures) 
    { 
     f = std::async(std::launch::async, doHeavyWork, index); 
     index++; 
    } 

    std::array<bool, numTasks> finishedTasks; 
    for(auto &i : finishedTasks) 
     i = false; 

    size_t numFinishedTasks = 0; 

    do 
    { 
     for(size_t i = 0; i < numTasks; ++i) 
     { 
      if(!finishedTasks[i] && isReady(futures[i])) 
      { 
       finishedTasks[i] = true; 
       numFinishedTasks++; 
       std::cout << "task " << i << " ended with result " << futures[i].get() << '\n'; 
      } 
     } 
    } 
    while(numFinishedTasks < numTasks); 

    std::cin.get(); 
} 

的std ::異步可以使用線程池啓動單獨的線程。

1

首先,你最好不要生成比核心更多的線程。

下面是使用分離並讓線程通知關於其狀態的簡單示例:

#include<thread> 
#include<atomic> 
struct task_data 
{ 

}; 
void doHeavyWork(task_data to_do, std::atomic<bool>& done) 
{ 
    //... do work 
    //--- 
    //-- 
    done = true; 
} 
int main() 
{ 
    unsigned int available_cores = std::thread::hardware_concurrency();//for real parallelism you should not spawn more threads than cores 
    std::vector<std::atomic<bool>> thread_statuses(available_cores); 
    for (auto& b : thread_statuses)//initialize with all cores/threads are free to use 
     b = true; 

    const unsigned int nb_tasks = 100;//how many? 
    std::vector<task_data> tasks_to_realize(nb_tasks); 
    for (auto t : tasks_to_realize)//loop on tasks to spawn a thread for each 
    { 
     bool found = false; 
     unsigned int status_id = 0; 
     while (!found) //loop untill you find a core/thread to use 
     { 
      for (unsigned int i = 0; i < thread_statuses.size(); i++) 
      { 
       if (thread_statuses[i]) 
       { 
        found = true; 
        status_id = i; 
        thread_statuses[i] = false; 
        break; 
       } 
      } 
     } 

     //spawn thread for this task 
     std::thread task_thread(doHeavyWork, std::move(t), std::ref(thread_statuses[status_id])); 
     task_thread.detach();//detach it --> you will get information it is done by it set done to true! 
    } 

    //wait till all are done 
    bool any_thread_running = true; 

    while (any_thread_running)//keep untill all are done 
    { 
     for (unsigned int i = 0; i < thread_statuses.size(); i++) 
     { 

      if (false == thread_statuses[i]) 
      { 
       any_thread_running = true; 
       break; 
      } 
      any_thread_running = false; 
     } 
    } 
    return 0; 
}