2017-05-08 6 views
2

나는 10 개의 스레드를 벡터로 푸시하는 프로그램이 있습니다. 각 스레드는 완료 전에 문자를 5 번 인쇄합니다 ('A'는 첫 번째 스레드, 두 번째 스레드의 'B'등). 나는 한 번에 (detach()를 사용하여) 모두 실행 시키거나 한번에 하나씩 (join()을 사용하여) 실행할 수있다. 이제 뮤텍스를 사용하여 한 번에 2 장까지 인쇄 할 수있는 스레드 수를 제한하려고합니다. 뮤텍스를 선언하고 잠금을 설정했지만이 제한을 적용하는 방법을 잘 모르겠습니다. . 누구든지 진행 방법에 대한 아이디어가 있습니까?뮤텍스를 사용하여 한 번에 실행되는 스레드 수를 제한합니다. 2

deque<int> q ; 
mutex print_mutex ; 
mutex queue_mutex ; 
condition_variable queue_cond ; 

void begin(int num) { 
    unique_lock<mutex> ul {queue_mutex}; 
    q.emplace_back(num); 
    queue_cond.wait(ul,[num]{ 
     return q.front() == num; }); 
    q.pop_front(); 
    cout << num << " leaves begin " << endl ; 
} 

void end (int num) { 
    lock_guard<mutex>lg{queue_mutex}; 
    queue_cond.notify_all(); 
    cout << num << " has ended " << endl ; 
} 

void run(int num, char ch) { 
    begin(num); 
    for (int i = 0; i < 5; ++i) { 
     { 
      lock_guard<mutex> lg { print_mutex }; 
      cout << ch << endl << flush ; 
     } 
     sleep_for(milliseconds(250)); 
    } 
    end(num); 
} 

int main() { 
    vector<thread>threads {}; 
    for (int i = 0; i < 10; ++i) { 
     threads.push_back(thread{run,i,static_cast<char>(65+i)}); 
     threads.at(i).join(); 
    } 
} 
+1

실행중인 스레드의 수를 계산하려면 정수가 필요합니다. –

+1

'detach()'와'join()'을 사용하여 시퀀싱 제어가 잘못되었습니다. 'main'의'for' 루프는 단순히 스레드를 직렬화합니다; 그것은 무의미하다. 루프를 사용하여 모든 ** 스레드를 작성한 다음 스레드의 ** 모든 ** 스레드를 결합하는 별도의 루프를 작성하십시오. 그런 다음 스레드가 상호 작용하는 방식을 파악하십시오. 'detach()'를 사용하지 마십시오; 확실히 당신이 필요로하는 것이 아닙니다. –

+0

@ PeteBecker 큰 문제는 내 문제였습니다! 훌륭한 설명. 더 나은 잠금 조건을 추가하려고 시도했지만 다른 루프로 결합을 분리 할 때까지 아무 것도 변하지 않았습니다. 계속 진행하고 싶다면 답변으로 받아 들일 수있어서 기쁩니다. – gmooney8

답변

3

당신은 이미 글로벌 deque<int> q와 스레드에 대한 FIFO를 설정했습니다. 그래서 그것을 사용합시다.

현재 현재 스레드가 전면에 위치 할 때까지 실행을 제한하려고합니다. begin은 즉시 deque에서 해당 스레드를 팝하기 때문에 버그가 있습니다. end으로 전화 할 때 값을 삭제하는 것이 좋습니다. 여기서 첫째, 그 변화의 :이 특정 값을 제거하는 <algorithm>에서 std::find를 사용

void end(int num) 
{ 
    { 
     lock_guard<mutex>lg{queue_mutex}; 
     cout << num << " has ended " << endl ; 
     q.erase(find(q.begin(), q.end(), num)); 
    } 
    queue_cond.notify_all(); 
} 

. pop_front을 사용할 수는 있지만,이 로직을 변경하여 더 일반적인 것입니다. 또한 통지 할 때 조건 변수를 잠그지 않아도됩니다.

begin의 로직을 처음 두 위치로 확장하는 것은 그리 중요하지 않습니다. 여기 :

void begin(int num) 
{ 
    unique_lock<mutex> ul {queue_mutex}; 
    q.emplace_back(num); 
    queue_cond.wait(ul,[num]{ 
     auto end = q.begin() + std::min(2, static_cast<int>(q.size())); 
     return find(q.begin(), end, num) != end; 
     }); 
    cout << num << " leaves begin " << endl ; 
} 

당신은 통과 많은 스레드까지 가능, 그 2 당신이 원하는 무엇이든을 변경할 수 있습니다. 언젠가는이 접근법을 포기하고 단일 카운터 변수처럼 더 단순한 것을 사용하고 스레드 스케줄러를 사용하여 FIFO에 강제로 넣는 대신 어떤 스레드가 깨어 있는지 관리 할 수 ​​있습니다. 그렇게하면 notify_one을 사용하여 단일 스레드를 깨우고 스위칭 오버 헤드를 줄일 수 있습니다.

어쨌든 마지막으로 할 일은 스레드 생성 루프에서 join을 제거하는 것입니다. 동시성은 이제 beginend에 의해 관리됩니다. 따라서 다음을 수행하십시오.

for (int i = 0; i < 10; ++i) { 
    threads.push_back(thread{run, i, 'A'+i}); 
} 
for (auto & t : threads) t.join(); 
+0

위대한 설명, 패 디! 나는 (당신이 언급 한 카운터를 사용하는) 다른 해결책을 찾지 만, 당신의 설명은 그것을 훨씬 잘 이해합니다. 고맙습니다! – gmooney8

+0

내가 언급 한 것을 잊어 버린 한 가지는 시작/종료 코드가 예외 안전이 아니라는 것입니다. 예외로 인해 스레드가 종료되면'end'가 호출되지 않습니다. RAII가 처리 할 수 ​​있도록 오브젝트 내부에서 begin/end 호출을 랩핑하는 것을 고려할 수 있습니다. – paddy