2016-10-15 2 views
2

모든 스레드가 완료 될 때까지 주 스레드를 차단하는 스레드 풀을 만들려고합니다. 실제 사용 사례는 사용자가 상호 작용할 수있는 독립적 인 프로세스를 생성하는 "컨트롤러"프로세스입니다.스레드 풀의 boost :: function 할당 해제 세그먼트 오류.

불행히도 main이 종료되면 분할 오류가 발생합니다. 이 세분화 오류의 원인을 파악할 수 없습니다.

나는 (A sleep 5를 포함 waiter.sh라고도 함) 쉘 스크립트를 열고 pid 종료하기를 기다리는 것보다 조금 더있는 Process 클래스를 작성했습니다. Process 클래스가 초기화 된 다음 Wait() 메서드가 스레드 풀의 스레드 중 하나에 배치됩니다.

~thread_pool()을 호출 할 때 문제가 발생합니다. Process에 대한 참조가 여전히 유효하더라도 std::queue은 전달 된 boost::function의 할당을 제대로 할당 해제 할 수 없습니다.

#include <sys/types.h> 
#include <sys/wait.h> 
#include <spawn.h> 

#include <queue> 
#include <boost/bind.hpp> 
#include <boost/thread.hpp> 

extern char **environ; 

class Process { 
private: 
    pid_t pid; 
    int status; 
public: 

    Process() : status(0), pid(-1) { 
    } 

    ~Process() { 
     std::cout << "calling ~Process" << std::endl; 
    } 

    void Spawn(char **argv) { 
     // want spawn posix and wait for th epid to return 
     status = posix_spawn(&pid, "waiter.sh", NULL, NULL, argv, environ); 
     if (status != 0) { 
      perror("unable to spawn"); 
      return; 
     } 
    } 

    void Wait() { 
     std::cout << "spawned proc with " << pid << std::endl; 
     waitpid(pid, &status, 0); 
     //  wait(&pid); 
     std::cout << "wait complete" << std::endl; 
    } 

}; 

아래는 thread_pool 클래스입니다. 이것은 느슨하게 다음은이 question

class thread_pool { 
private: 
    std::queue<boost::function<void() >> tasks; 
    boost::thread_group threads; 
    std::size_t available; 
    boost::mutex mutex; 
    boost::condition_variable condition; 
    bool running; 
public: 

thread_pool(std::size_t pool_size) : available(pool_size), running(true) { 
    std::cout << "creating " << pool_size << " threads" << std::endl; 
    for (std::size_t i = 0; i < available; ++i) { 
     threads.create_thread(boost::bind(&thread_pool::pool_main, this)); 
    } 
} 

~thread_pool() { 
    std::cout << "~thread_pool" << std::endl; 
    { 
     boost::unique_lock<boost::mutex> lock(mutex); 
     running = false; 
     condition.notify_all(); 
    } 

    try { 
     threads.join_all(); 
    } catch (const std::exception &) { 
     // supress exceptions 
    } 
} 

template <typename Task> 
void run_task(Task task) { 

    boost::unique_lock<boost::mutex> lock(mutex); 
    if (0 == available) { 
     return; //\todo err 
    } 

    --available; 

    tasks.push(boost::function<void()>(task)); 
    condition.notify_one(); 
    return; 
} 

private: 

void pool_main() { 

    // wait on condition variable while the task is empty and the pool is still 
    // running 
    boost::unique_lock<boost::mutex> lock(mutex); 
    while (tasks.empty() && running) { 
     condition.wait(lock); 
    } 

    // copy task locally and remove from the queue. this is 
    // done within it's own scope so that the task object is destructed 
    // immediately after running the task. This is useful in the 
    // event that the function contains shared_ptr arguments 
    // bound via 'bind' 
    { 
     auto task = tasks.front(); 
     tasks.pop(); 

     lock.unlock(); 

     // run the task 
     try { 
      std::cout << "running task" << std::endl; 
      task(); 
     } catch (const std::exception &) { 
      // supress 
     } 
    } 

    // task has finished so increment count of availabe threads 
    lock.lock(); 
    ++available; 

    } 
}; 

에 대한 허용 대답에서 적응하는 것은 기본입니다 :

int main() { 

    // input arguments are not required 
    char *argv[] = {NULL}; 
    Process process; 
    process.Spawn(argv); 

    thread_pool pool(5); 

    pool.run_task(boost::bind(&Process::Wait, &process)); 

    return 0; 
} 

이의 출력이

creating 5 threads 
~thread_pool 
I am waiting... (from waiting.sh) 
running task 
spawned proc with 2573 
running task 
running task 
running task 
running task 
wait complete 
Segmentation fault (core dumped) 

입니다 그리고 여기 스택 추적입니다 :

Starting program: /home/jandreau/NetBeansProjects/Controller/dist/Debug/GNU- Linux/controller 
[Thread debugging using libthread_db enabled] 
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". 
creating 5 threads 
[New Thread 0x7ffff691d700 (LWP 2600)] 
[New Thread 0x7ffff611c700 (LWP 2601)] 
[New Thread 0x7ffff591b700 (LWP 2602)] 
[New Thread 0x7ffff511a700 (LWP 2603)] 
[New Thread 0x7ffff4919700 (LWP 2604)] 
~thread_pool 
running task 
running task 
spawned proc with 2599 
[Thread 0x7ffff611c700 (LWP 2601) exited] 
running task 
[Thread 0x7ffff591b700 (LWP 2602) exited] 
running task 
[Thread 0x7ffff511a700 (LWP 2603) exited] 
running task 
[Thread 0x7ffff4919700 (LWP 2604) exited] 
I am waiting... 
wait complete 
[Thread 0x7ffff691d700 (LWP 2600) exited] 

Thread 1 "controller" received signal SIGSEGV, Segmentation fault. 
0x000000000040f482 in boost::detail::function::basic_vtable0<void>::clear (
this=0xa393935322068, functor=...) 
at /usr/include/boost/function/function_template.hpp:509 
509   if (base.manager) 
(gdb) where 
#0 0x000000000040f482 in boost::detail::function::basic_vtable0<void>::clear (
this=0xa393935322068, functor=...) 
at /usr/include/boost/function/function_template.hpp:509 
#1 0x000000000040e263 in boost::function0<void>::clear (this=0x62ef50) 
at /usr/include/boost/function/function_template.hpp:883 
#2 0x000000000040cf20 in boost::function0<void>::~function0 (this=0x62ef50, 
__in_chrg=<optimized out>) 
at /usr/include/boost/function/function_template.hpp:765 
#3 0x000000000040b28e in boost::function<void()>::~function() (
this=0x62ef50, __in_chrg=<optimized out>) 
at /usr/include/boost/function/function_template.hpp:1056 
#4 0x000000000041193a in std::_Destroy<boost::function<void()> >(boost::function<void()>*) (__pointer=0x62ef50) 
at /usr/include/c++/5/bits/stl_construct.h:93 
#5 0x00000000004112df in std::_Destroy_aux<false>::__destroy<boost::function<void()>*>(boost::function<void()>*, boost::function<void()>*) (
__first=0x62ef50, __last=0x62ed50) 
at /usr/include/c++/5/bits/stl_construct.h:103 
#6 0x0000000000410d16 in std::_Destroy<boost::function<void()>*>(boost::function<void()>*, boost::function<void()>*) (__first=0x62edd0, __last=0x62ed50) 
at /usr/include/c++/5/bits/stl_construct.h:126 
#7 0x0000000000410608 in std::_Destroy<boost::function<void()>*, boost::function<void()> >(boost::function<void()>*, boost::function<void()>*, std::allocat---Type <return> to continue, or q <return> to quit--- 
or<boost::function<void()> >&) (__first=0x62edd0, __last=0x62ed50) 
at /usr/include/c++/5/bits/stl_construct.h:151 
#8 0x000000000040fac5 in std::deque<boost::function<void()>, std::allocator<boost::function<void()> > >::_M_destroy_data_aux(std::_Deque_iterator<boost::function<void()>, boost::function<void()>&, boost::function<void()>*>, std::_Deque_iterator<boost::function<void()>, boost::function<void()>&, boost::function<void()>*>) (this=0x7fffffffdaf0, __first=..., __last=...) 
at /usr/include/c++/5/bits/deque.tcc:845 
#9 0x000000000040e6e4 in std::deque<boost::function<void()>, std::allocator<boost::function<void()> > >::_M_destroy_data(std::_Deque_iterator<boost::function<void()>, boost::function<void()>&, boost::function<void()>*>, std::_Deque_iterator<boost::function<void()>, boost::function<void()>&, boost::function<void()>*>, std::allocator<boost::function<void()> > const&) (
this=0x7fffffffdaf0, __first=..., __last=...) 
at /usr/include/c++/5/bits/stl_deque.h:2037 
#10 0x000000000040d0c8 in std::deque<boost::function<void()>, std::allocator<boost::function<void()> > >::~deque() (this=0x7fffffffdaf0, 
__in_chrg=<optimized out>) at /usr/include/c++/5/bits/stl_deque.h:1039 
#11 0x000000000040b3ce in std::queue<boost::function<void()>, std::deque<boost::function<void()>, std::allocator<boost::function<void()> > > >::~queue() (
this=0x7fffffffdaf0, __in_chrg=<optimized out>) 
at /usr/include/c++/5/bits/stl_queue.h:96 
#12 0x000000000040b6c0 in thread_pool::~thread_pool (this=0x7fffffffdaf0, 
---Type <return> to continue, or q <return> to quit--- 
__in_chrg=<optimized out>) at main.cpp:63 
#13 0x0000000000408b60 in main() at main.cpp:140 

Process이 아직 범위를 벗어나지 않았기 때문에 boost::function<void()> 사본을 처리를 위해 스레드 풀로 전달하기 때문에 나는 이것에 의아합니다.

아이디어가 있으십니까?

+0

'pool_main'에서'tasks'에 대한 액세스를 동기화하면 안됩니까? – JohnB

+0

@ 존경 어떻게해야합니까? 나는 초보 신입생이다. –

+0

'tasks'에 접근하기 전에'lock.lock()'을 호출하십시오. – JohnB

답변

1

스택 추적은 올바르게 초기화되지 않은 std::function (예 : std::function으로 취급되는 임의의 임의의 메모리 위치)을 파괴하고 있거나 std::function을 두 번 파괴하고 있음을 나타냅니다.

문제는 따라서 당신은 정의되지 않은 동작입니다 빈 양단 큐로부터 요소를 제거, 프로그램이 한 번만 tasks에 밀어 것입니다,하지만 다섯 번 나옵니다. running 거짓이면 pool_main

while 루프는 종료하고 runningdeque가 비어있는 경우에도 오류가있을 수있다. 그럼 무조건 당신 pop. 다음과 같이 pool_main를 수정하는 것이 좋습니다 :

void pool_main() { 

    // wait on condition variable 
    // while the task is empty and the pool is still 
    // running 
    boost::unique_lock<boost::mutex> lock(mutex); 
    while (tasks.empty() && running) { 
     condition.wait(lock); 
    } 

    // copy task locally and remove from the queue. this is 
    // done within it's own scope so that the task object is destructed 
    // immediately after running the task. This is useful in the 
    // event that the function contains shared_ptr arguments 
    // bound via 'bind' 
    if (!tasks.empty()) { // <--- !!!!!!!!!!!!!!!!!!!!!!!! 
     auto task = tasks.front(); 
     tasks.pop(); 

     lock.unlock(); 

     // run the task 
     try { 
      std::cout << "running task" << std::endl; 
      task(); 
     } catch (const std::exception &) { 
      // supress 
     } 
    } 

    // task has finished so increment count of availabe threads 
    lock.lock(); 
    ++available; 
}; 

나는, 그러나, available에 대한 논리가 올바른지 확실하지. 태스크의 처리를 시작할 때 available을 감소시켜야하고, 완료되면 증가해야합니다 (따라서 pool_main에서만 변경되고 새로 도입 된 if 절에서만 변경됩니다).

+0

사용 가능 (이 경우)은 풀에서 사용할 수있는 스레드의 양입니다. 하나의 스레드가 완료 될 때까지 실행되면 스레드는 작업 대기열에서 빠져 나와 다시 사용 가능하게됩니다. 'tasks.empty() '를 가진 당신의 솔루션은 문제를 적절하게 해결했습니다. 도와 줘서 고마워. –

0

당신은 어디

extern char **environ; 

에 대한 메모리를 할당 할 것 같지 않습니다. 링크 오류가 아니겠습니까?

최소한의 복제 사례로 이것을 자르면 많은 도움이됩니다. 여기에는 문제를 재현 할 필요가없는 코드가 많이 있습니다.

// supress exceptions 

당신의 스레드에 가입하는 동안 당신은 아마도 그들 모두에 가입하고 가입없이 스레드를 정리하지 않은 예외를 받고있는 경우는 주 후에 오류가 발생합니다 :

또한, 무슨 일이 이것이다 종료.