내 작업자 [스레드] 클래스에 일시 중지/다시 시작 기능을 추가하려는 시도에서 설명 할 수없는 문제가 발생했습니다. (C++ 1y/VS2015)작업자 스레드 일시 중단/재개 구현
문제는 교착 상태처럼 보입니다. 그러나 일단 디버거가 연결되고 특정 지점 (# 1 참조) 전에 중단 점이 설정되면이를 재현 할 수 없습니다. 그것은 타이밍 문제입니다.
뮤텍스를 길게 잡고 클라이언트 코드가 다른 뮤텍스를 획득하려고 시도 할 수 있기 때문에 내가 (# 2) 찾을 수있는 수정 사항이 많은 의미를 갖지 않는다. 교착 상태의 가능성을 높입니다.
하지만 문제가 해결됩니다.
작업자 루프 :
Job* job;
while (true)
{
{
std::unique_lock<std::mutex> lock(m_jobsMutex);
m_workSemaphore.Wait(lock);
if (m_jobs.empty() && m_finishing)
{
break;
}
// Take the next job
ASSERT(!m_jobs.empty());
job = m_jobs.front();
m_jobs.pop_front();
}
bool done = false;
bool wasSuspended = false;
do
{
// #2
{ // Removing this extra scoping seemingly fixes the issue BUT
// incurs us holding on to m_suspendMutex while the job is Process()ing,
// which might 1, be lengthy, 2, acquire other locks.
std::unique_lock<std::mutex> lock(m_suspendMutex);
if (m_isSuspended && !wasSuspended)
{
job->Suspend();
}
wasSuspended = m_isSuspended;
m_suspendCv.wait(lock, [this] {
return !m_isSuspended;
});
if (wasSuspended && !m_isSuspended)
{
job->Resume();
}
wasSuspended = m_isSuspended;
}
done = job->Process();
}
while (!done);
}
가 일시 중단/다시 시작은 다음과 같습니다
void Worker::Suspend()
{
std::unique_lock<std::mutex> lock(m_suspendMutex);
ASSERT(!m_isSuspended);
m_isSuspended = true;
}
void Worker::Resume()
{
{
std::unique_lock<std::mutex> lock(m_suspendMutex);
ASSERT(m_isSuspended);
m_isSuspended = false;
}
m_suspendCv.notify_one(); // notify_all() doesn't work either.
}
(비주얼 스튜디오) 테스트 :/잘못하고
내가 놓친 게 무엇struct Job: Worker::Job
{
int durationMs = 25;
int chunks = 40;
int executed = 0;
bool Process()
{
auto now = std::chrono::system_clock::now();
auto until = now + std::chrono::milliseconds(durationMs);
while (std::chrono::system_clock::now() < until)
{ /* busy, busy */
}
++executed;
return executed < chunks;
}
void Suspend() { /* nothing here */ }
void Resume() { /* nothing here */ }
};
auto worker = std::make_unique<Worker>();
Job j;
worker->Enqueue(j);
std::this_thread::sleep_for(std::chrono::milliseconds(j.durationMs)); // Wait at least one chunk.
worker->Suspend();
Assert::IsTrue(j.executed < j.chunks); // We've suspended before we finished.
const int testExec = j.executed;
std::this_thread::sleep_for(std::chrono::milliseconds(j.durationMs * 4));
Assert::IsTrue(j.executed == testExec); // We haven't moved on.
// #1
worker->Resume(); // Breaking before this call means that I won't see the issue.
worker->Finalize();
Assert::IsTrue(j.executed == j.chunks); // Now we've finished.
? 왜 작업의 프로세스()는 suspend
뮤텍스에 의해 지켜 져야합니까?
EDIT : Resume()
은 알림시 뮤텍스를 유지하지 않아야합니다. 문제가 해결되지 않았습니다.