manual reset과 auto reset의 차이점에 대해

kid1402의 이미지

WinAPI 의 Event 생성 함수인 CreateEvent의 두번째 인자에 따라 생성되는 이벤트 객체가 Manual-reset인지 Auto-reset인지 구별됩니다.

일단 manual-reset은 SetEvent() 후에 반드시 어디선가든 ResetEvent가 불려져야 이벤트 객체가 Reset되는것이고,
auto-reset은 SetEvent()를 하면 WaitForSingleObject()로 기다리는 여러개의 스레드 중 단 하나의 스레드만이 이 이벤트의 signaled state를 알아 차리고(정확히는 OS가 스케쥴링 해줘서) 작업 후에 자동으로 Reset되죠..

msdn에는 딱히 언급은 없습니다만, 이미 Reset된 이벤트를 여러번 Reset하는 것은 영향을 주지 않는다는 설명이 있어서, 스레드간의 동기화를 논리적으로 더 매끄럽게 하기 위해

작업 큐에 넣는 스레드에서는 객체 초기화시에 AUTO-RESET형 이벤트를 생성한 뒤 작업 요청이 있을 때마다 SetEvent()를 호출하고, 작업 스레드쪽에서 WaitForSingleObject()로 기다리다가 작업을 다 처리하면 ResetEvent()를 호출하는 방식으로 코드를 짰습니다.

일단 제 생각에는 auto-reset형 이벤트니까 작업 스레드 쪽에서 wait을 받아 처리하고 다 끝나면 OS단에서 자동으로 Reset시켜줄거고, 해당 스레드 내에서 Reset이 불리긴 하지만 어차피 Reset된 걸 다시 Reset하는건 영향을 주지 않는다고 했으므로 안전한 코드라고 생각했습니다.

그런데 SetEvent를 호출하는 함수를 매우 많이.. 초당 1000번정도 빈번하게 호출하는 경우 엄청 높은 확률로 Event 간의 데드락이 발생합니다. 데드락이라고 확신하는 이유는 이미 다른 로직(queue에 대한 push, pop 등)에 대한 테스트를 많이 거쳤고, WaitForSingleObject()에 대기시간을 무한으로 설정하지 않았을 경우 TIMEOUT이 나기 때문입니다.

제가 알기로는 분명 Event관련 함수는 Atomic 한 동작을 보장하고, 또한 Reset된 이벤트를 다시 Reset 하는것은 아무런 영향을 미치지 않는다고 하는데 뭐가 잘못된 걸까요?
auto-reset 형 이벤트를 Reset하는 게 데드락의 직접적인 원인이라고 생각하지만, 심증만 있고 물증은 없는 상황이라 더 답답하네요..
정확한 이유를 알려주실 분 계신가요?

gilgil의 이미지

Windows OS에서의 event 객체는 mutex, wait condition의 조합으로 구현될 수 있습니다.
pseudo code를 올리니 참고하시기 바랍니다.
Windows 에서 Linux로 포팅할 때 사용하는 Event 클래스이며, Qt 기반이지만 linux용으로 쉽게 포팅될 수 있습니다.

// ----------------------------------------------------------------------------
// VEvent
// ----------------------------------------------------------------------------
class VEvent
{
protected:
  QMutex         m_mutex;
  QWaitCondition m_cond;
  bool           m_manualReset;
  bool           m_state;
 
public:
  VEvent(bool manualReset = false, bool initialState = false);
  virtual ~VEvent();
 
public:
  bool state() { return m_state; }
  void setEvent();
  void resetEvent();
  bool wait(unsigned long timeout = ULONG_MAX);
};

// ----------------------------------------------------------------------------
// VEvent
// ----------------------------------------------------------------------------
VEvent::VEvent(bool manualReset, bool initialState)
{
  m_manualReset = manualReset;
  m_state       = initialState;
}
 
VEvent::~VEvent()
{
}
 
void VEvent::setEvent()
{
  m_mutex.lock();
  m_state = true;
  m_cond.wakeAll();
  m_mutex.unlock();
}
 
void VEvent::resetEvent()
{
  m_state = false;
}
 
bool VEvent::wait(unsigned long timeout)
{
  m_mutex.lock();
  bool res = true;
  if (!m_state)
  {
    res = m_cond.wait(&m_mutex, timeout);
  }
  if (!m_manualReset) m_state = false;
  m_mutex.unlock();
  return res;
}

Windows OS에서
1. manualReset을 false로 생성 > wait call > resetEvent call
2. manualReset을 true로 생성 > wait call
1번과 2번은 비슷하게 보이지만 1번에서 wait call과 resetEvent call이 atomic하지 않기 때문에 별도로 manualReset기능(2번)을 지원하는 것입니다.

익명 사용자의 이미지

설명하신 내용 중에서 1,2가 아래와 같이 돼야할 것 같습니다.

1. manualReset을 false로 생성 > wait call
2. manualReset을 true로 생성 > wait call > resetEvent call

gilgil의 이미지

그렇군요, 제가 반대로 잘못 적었습니다. ^^

익명 사용자의 이미지

전에 올리신 질문을 참조하여 답변 드리자면..
manualReset이 false로 되어 있다고 가정하구요..
push가 짧은 시간에 2번 불렸다고 해봅시다. (매우 짧은 시간이라서 그 사이에 pop이 일어나지 않았다고 가정하구요)
그 후에 2개의 pop thread에서 각각 pop을 호출한다면, 작성자님이 원하시는 시나리오는 2개의 data가 각각 pop되어 처리되는 것일 겁니다.
하지만 작성자님의 코드상으로는 2개의 pop thread 중 하나만 WaitForSingleObject에서 빠져나올 수 있겠죠.
즉, push가 여러번 불려도 pop이 실제로는 그만큼 되지 않는 현상이 발생할 수 있는 겁니다.

문제의 핵심은 setEvent는 1번 불리나 2번 불리나 n번 불리나 효과에는 차이가 없다는 겁니다. (그 사이에 WaitForSingleObject를 부른 thread가 없다고 가정할때)
따라서 제 생각에는 semaphore를 사용해야 하는 경우가 아닌가 싶네요.

gilgil의 이미지

원 질문으로 다시 가서 말씀드리자면 본 스레드의 문제점은 pop thread가 여러개인 것이 문제점입니다.
push thread(데이터 넣어 주는 놈)가 여러개고 pop thread(데이터 빼 가는 놈)가 하나인 경우는 이해가 되는데, 그 반대의 경우는 이상하다는 거죠.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.