(멀티 쓰레드 통신) Conditional Variable의 wait 함수의 대기상태에서 다시 활성화되지 않는 문제

standalon의 이미지

멀티 쓰레드 통신 프로그램에서 Conditional Variable의 wait 함수를 실행해서 대기 상태인 쓰레드가
해당 조건이 만족되었는데도 불구하고, 다시 활성화되지 않는 문제가 발생합니다.
이러한 문제가 항상 발생하는 것은 아니고 가끔씩 발생되어, 프로그램이 대기 상태로 더 이상 진행되지 않습니다.
고수분들의 많은 조언 부탁드립니다.

본 프로그램은 2개의 노드가 연산된 데이터를 송수신하면서 실행하는 프로그램입니다.
각 노드는에서는 5개의 쓰레드가 실행되며 그 역할은 다음과 같습니다.
1. 메인 쓰레드
2. 송신 쓰레드 : 데이터 송신을 담당하는 쓰레드
3. 수신 쓰레드 : 데이터 수신을 담당하는 쓰레드
4. 연산 쓰레드(2개) : 데이터의 연산을 담당하고 연산 효율성을 위해서 멀티 쓰레드로 작동함.

문제가 발생하는 데이터 수신 부분의 자세한 설명은 다음과 같습니다.

Step A) 데이터를 수신하려는 연산 쓰레드는 Received Queue에 연산쓰레드의 ID(2 바이트)에 해당하는 데이터 들어올때까지 대기합니다.

ulRecv.lock();
RecvCV->wait(ulRecv, [&] {		
    RecvPtr = RecvQueue->front();
    return !RecvQueue->empty() && RecvPtr[0]==(idx&0xff) && RecvPtr[1]==(idx>>8);
    });

Step B) 수신 쓰레드에서 데이터를 수신하면, (Mutex의 lock실행) Received Queue에 데이터를 Push하고 (Mutex의 unlock실행) notify를 실행합니다.

try {
    SRecvSocket->recv(RecvBuf, Len);
}
catch ( SocketException& ) {}
 
RecvMtx->lock();
RecvQueue->push(RecvBuf);
RecvMtx->unlock();
 
RecvCV->notify_all();



Step C) 수신 쓰레드의 notify_all 함수에 의해서, Received Queue에 해당 연산쓰레드의 ID에 해당하는 데이터가 있음을 확인하게 되면, 연산 쓰레드는 대기 상태에서 해제되고 해당 데이터를 Pop해서 연산을 수행하게 됩니다.

RecvPtr = RecvQueue->front();
RecvQueue->pop();
ulRecv.unlock();

문제는 (항상은 아니고 가끔씩) step B에서 step C과정으로 넘어가지 않는다는 것입니다.
데이터 로그를 통해서 step A와 step B의 과정이 모두 실행되었음을 확인하였는데,
대기 상태인 연산 쓰레드가 가끔씩 다시 활성화가 되지 않습니다.
그런데, 연산 쓰레드를 하나의 쓰레드(전체 4개의 쓰레드)로 실행할 경우에는 이러한 문제가 발생하지 않습니다.

어떻게 해결해야 할지 감이 안잡히네요.
고수 분들의 조언 부탁드립니다.
감사합니다.

김강진의 이미지

Step C 를 notify_all 로 깨우지 마시고, 계속 queue 가 empty 인지를 계속 확인하도록 변경하세요. while loop 이나 timer 등을 사용하면 좋을것 같습니다. 그러면 notify 할 일이 없습니다.

그리고, Step C 가 queue 에서 element 를 pop 한 후 해당 element 에 해당하는 process 를 위해서 1회성의 new thread 를 시작하면 될듯 합니다. 문제는 새로운 thread 가 끝나기 전에 또 다른 thread 가 실행될 수 있습니다. 이것은 queue size 를 제어하듯 thread 의 수를 제어하여 pool 를 관리하는 것으로 해결해야 합니다. 도움이 되었기를 바랍니다.

jick의 이미지

> Step C 를 notify_all 로 깨우지 마시고, 계속 queue 가 empty 인지를 계속 확인하도록 변경하세요. while loop 이나 timer 등을 사용하면 좋을것 같습니다.

이건 문제를 푸는 게 아니라 문제 해결을 포기하는 것 아닙니까. -_-

김강진의 이미지

notify all 이라는게 어짜피 sleep 상태에서 깨우실 텐데, 그렇다면 coroutine 을 사용하시면 될것 같네요.

chrak의 이미지

Conditional Variable은 사용해 본 적 없지만 event의 waitforsingleobject와 메커니즘이 같은
내용으로 보이는데요...
깨울 때 step C로 안남어 가지는 않을 것 같고 notify_all을 하여 쓰레드를 깨운 상태에서 notify_all이
또 실행되어 굳이 깨울 필요 없는 서버를 다시 꺠우면서 일어난 문제가 아닌가 생각됩니다.

step C에서 루프를 돌려서 큐에 있는 내용을 전부 실행시키게 하면 되지 않을까요?

chrak의 이미지

서버 -> 쓰레드 ^^;;;

Anti-Lock의 이미지

ulRecv 이 유니크락인거 같은데, RecvMtx 뮤텍스를 건드리도록 생성된 것이 맞나요?
A 단계에서는 데이터가 있는지만 체크하고,
C 단계에서 원하는 데이터가 있는지 체크하는 것으로 바꿔야 할거 같습니다.
현상태의 코드에서는, wait 에서 원하고 있는 데이터가 아닌 다른 데이터가 들어온 경우,
데이터가 소모되지(pop) 않으며, 계속 대기 상태로 들어가게 됩니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.