2016-08-27 1 views
0

안녕에 세그 폴트. 작업을 처리 할 수있는 스레드 풀을 만들려고합니다. 관련된 테스트 메인 아래에 있습니다.C++ 동시성</p> <p>내가 C 아주 새로운 오전 ++하지만 육년 자바 경험, 2 년 C 경험과 동시성 몇 가지 기본 지식을 가지고, 뮤텍스

내 디버거에 의해 말했듯이 오류가

void ThreadPool::ThreadHandler::enqueueTask(void (*task)(void)) { 
    std::lock_guard<std::mutex> lock(queueMutex); 

에서 생성되지만 기존의 cout을 디버그하고, 나는 때때로 segfaulting으로부터

threads.emplace(handler->getSize(), handler); 

를 제거하지 않고 작동하는지 발견 것 같아 ThreadPool::enqueueTask()은 안정성을 크게 향상시킵니다.

전반적으로 나는 conditioner_variable (idler라고 불리는)의 나쁜 사용과 관련이 있다고 생각합니다.

컴파일러 : CLion에서는 MinGW-W64

#include <iostream> 
#include "ThreadPool.h" 

ThreadPool::ThreadHandler::ThreadHandler(ThreadPool *parent) : parent(parent) { 
    thread = std::thread([&]{ 
     while (this->parent->alive){ 
      if (getSize()){ 
       std::lock_guard<std::mutex> lock(queueMutex); 
       (*(queue.front()))(); 
       queue.pop_front(); 
      } else { 
       std::unique_lock<std::mutex> lock(idlerMutex); 
       idler.wait(lock); 
      } 
     } 
    }); 
} 

void ThreadPool::ThreadHandler::enqueueTask(void (*task)(void)) { 
    std::lock_guard<std::mutex> lock(queueMutex); 
    queue.push_back(task); 
    idler.notify_all(); 
} 

size_t ThreadPool::ThreadHandler::getSize() { 
    std::lock_guard<std::mutex> lock(queueMutex); 
    return queue.size(); 
} 

void ThreadPool::enqueueTask(void (*task)(void)) { 
    std::lock_guard<std::mutex> lock(threadsMutex); 
    std::map<int, ThreadHandler*>::iterator iter = threads.begin(); 
    threads.erase(iter); 
    ThreadHandler *handler = iter->second; 
    handler->enqueueTask(task); 
    threads.emplace(handler->getSize(), handler); 
} 

ThreadPool::ThreadPool(size_t size) { 
    for (size_t i = 0; i < size; ++i) { 
     std::lock_guard<std::mutex> lock(threadsMutex); 
     ThreadHandler *handler = new ThreadHandler(this); 
     threads.emplace(handler->getSize(), handler); 
    } 
} 

ThreadPool::~ThreadPool() { 
    std::lock_guard<std::mutex> lock(threadsMutex); 
    auto it = threads.begin(), end = threads.end(); 
    for (; it != end; ++it) { 
     delete it->second; 
    } 
} 

.H

#ifndef WLIB_THREADPOOL_H 
#define WLIB_THREADPOOL_H 

#include <mutex> 
#include <thread> 
#include <list> 
#include <map> 
#include <condition_variable> 

class ThreadPool { 
private: 
    class ThreadHandler { 
     std::condition_variable idler; 
     std::mutex idlerMutex; 
     std::mutex queueMutex; 
     std::thread thread; 
     std::list<void (*)(void)> queue; 
     ThreadPool *parent; 
    public: 
     ThreadHandler(ThreadPool *parent); 
     void enqueueTask(void (*task)(void)); 
     size_t getSize(); 
    }; 
    std::multimap<int, ThreadHandler*> threads; 
    std::mutex threadsMutex; 
public: 
    bool alive = true; 
    ThreadPool(size_t size); 
    ~ThreadPool(); 

    virtual void enqueueTask(void (*task)(void)); 
}; 


#endif //WLIB_THREADPOOL_H 

주요 통화 당은 :

#include <iostream> 
#include <ThreadPool.h> 

ThreadPool pool(3); 

void fn() { 
    std::cout << std::this_thread::get_id() << '\n'; 
    pool.enqueueTask(fn); 
}; 

int main() { 
    std::cout << "Hello, World!" << std::endl; 
    pool.enqueueTask(fn); 
    return 0; 
} 
+0

위 예제가 절대로 종료되지 않는다는 사실을 알고 있습니까? 작업은 항상 다른 작업을 시작합니다. – Steve

+0

'threads' 맵은 결코 비어 있지 않습니까? 그리고 "function"에 대해 ['std :: function'] (http://en.cppreference.com/w/cpp/utility/functional/function)을 사용하지 않는 이유는 무엇입니까? –

+0

@Steve 예, 더 오랜 기간 테스트하는 것입니다. – Wicpar

답변

5

귀하의 main() 기능이를 호출.

즉시, main()이 반환됩니다.

이것은 프로세스를 감아 올리기위한 기어를 얻습니다. 여기에는 모든 전역 개체의 소멸자를 호출하는 작업이 포함됩니다.

ThreadPool의 소멸자는 모든 동적 범위 스레드를 삭제합니다.

스레드가 계속 실행되는 동안. Hilarity가 계속됩니다.

모든 스레드를 순서대로 종료하려면 프로세스를 구현해야합니다.

이것은 자연을 코스로 만들기 전에 모든 스레드를 연결하고 마지막으로 모든 것을 파괴하는 거짓으로 모든 스레드를 걷어차 고 거짓으로 설정하는 것을 의미합니다.

P. - alive의 검사 방법을 수정해야합니다. 또한 mutex로 보호되는 alive 스레드 안전에 액세스해야합니다. 문제는 스레드가 두 개의 다른 뮤텍스 중 하나에서 잠금을 보유 할 수 있다는 것입니다. 이것은이 과정을 다소 복잡하게 만든다. 일부 재 설계가 순서대로 진행됩니다.

+0

실제로 그것은 소멸자입니다, 고맙습니다. 많은 영향을받을 것이라고 생각하지 않았습니다. 부울은 한번만 false로 변경되고, 프리미티브의 읽기/쓰기는 데이터 유형에 즉각적이므로 스레드 안전성이 필요합니다. – Wicpar

+0

"스레드 안전성"은 "여러 스레드의 여러 업데이트가 스레드 안전 방식 "뿐만 아니라"하나의 스레드에서 업데이트하고 다른 스레드에서 읽는 방법에 대해 잘 정의 된 의미 "를 제공합니다. 뮤텍스가 유지되는 동안 발생하는 변경 사항 만 뮤텍스가 해제 된 후에 다른 스레드가 볼 수 있도록 보장됩니다 (다른 스레드도 뮤텍스를 보유 함). –

+0

나는 아직도 그 케이스의 사용법을 보지 못한다. 아키텍처는 부모의 생동감을 점검함으로써 시작되는 작업주기를 정의한다. 살아있는 것이라면 그것의 일을한다. 그렇지 않다면 끝낸다. 뮤텍스에 의한 성능 감소가 추가적인주기를 초래할 수 있음에 따라 어떻게 보안을 제공하는지 보지 못했습니다 ... 하나의 스레드가 관찰 할 수있는 효과가 보장되지 않는 경우 하나의 명령을 남겨 두지 않을 것입니다 ... – Wicpar