Synchronization: The Reader Writer Problem
Angrave System Programming/Synchronization 2019. 1. 17. 14:54What is the Reader Writer Problem?
많은 스레드가 사용하는 Key-value map 자료구조가 있다고 생각해봅시다. 여러개의 스레드는 제공되어지는 자료구조가 써지고 있지 않을 때 찾아봐야 합니다. data corruption을 막기 위해서, 한 스레드만이 자료구조를 수정할 수 있어야 합니다(아무도 읽고있지도 않아야 합니다)
이것이 Reader Write Problem의 예입니다. 즉, 여러명의 reader와 write를 어떻게 효율적으로 동기화시키는지에 대한 문제입니다. 예를 들어 여러명의 reader가 같이 읽을 수는 있지만 writer는 독접적인 접근을 할 수 있게 해야합니다.
다음의 예는 정확하지 않은 시도를 보여주고 있습니다(lock은 pthread_mutex_lock의 줄임말입니다)
Attempt #1
read() { lock(&m) // do read stuff unlock(&m) } | write() { lock(&m) // do write stuff unlock(&m) } |
적어도 첫 시도는 data corruption이 일어나지 않습니다.(reader는 writer가 쓰는동안 기다려야 하고, 그 반대도 마찬가지입니다) 하지만 reader가 다른 reader도 기다려야 합니다. 다른 시도를 해봅시다.
Attempt #2
read() { while(writing) {/*spin*/} reading = 1 // do read stuff reading = 0 } | write() { while(reading || writing) {/*spin*/} writing = 1 // do write stuff writing = 0 } |
Attempt #3
read() { lock(&m) while (writing) cond_wait(&cv, &m) reading++; /* Read here! */ reading-- cond_signal(&cv) unlock(&m) }
하지만 mutex를 unlock하지 않기 때문에 한 번에 한 독자만 읽을 수 있습니다.
더 나은 방법은 reading 전에 unlock하는 것입니다.
read() { lock(&m); while (writing) cond_wait(&cv, &m) reading++; unlock(&m) /* Read here! */ lock(&m) reading-- cond_signal(&cv) unlock(&m) }
이렇게 하면 writer와 reader는 동시에 읽거나 쓸 수 없을까요? 아닙니다.
일단 cond_wait이 리턴되기 전에 mutex lock을 다시 얻는 과정이 필요합니다. 그러므로 한 번에 한 스레드만이 CS(**로 표시됨) 안의 코드를 실행할 수 있습니다.
read() { lock(&m); ** while (writing) ** cond_wait(&cv, &m) ** reading++; unlock(&m) /* Read here! */ lock(&m) ** reading-- ** cond_signal(&cv) unlock(&m) }
Writer는 모두를 기다려야 합니다. lock으로 Mutual exclusion을 보장해주어야 합니다.
write() { lock(&m); ** while (reading || writing) ** cond_wait(&cv, &m); ** writing++; ** ** /* Write here! */ ** writing--; ** cond_signal(&cv); unlock(&m); }
위에 #3번도 하나의 스레드만 깨워주는 ptherad_cond_siganl을 사용합니다. 예를 들어, 많은 reader가 writer가 쓰기를 기다리고 있다면 한 명의 독자만이 잠에서 깨어납니다. reader와 writer는 cond_broadcast를 사용해 모든 스레드가 일어나 while-loop condition을 확인하도록 해야 합니다.
Starving writers
write() { lock() writer++ while (reading || writing) cond_wait unlock() ... }
그리고 나서 들어오는 reader들은 writer가 0이 아닐 동안 진입하지 못합니다.
writer는 writer가 도착했을음 의미하고, reading과 writing은 active reader나 writer가 있음을 의미합니다.
read() { lock() // readers that arrive *after* the writer arrived will have to wait here! while(writer) cond_wait(&cv,&m) // readers that arrive while there is an active writer // will also wait. while (writing) cond_wait(&cv,&m) reading++ unlock ... }
Attempt #4
reader() { lock(&m) while (writers) cond_wait(&turn, &m) // No need to wait while(writing here) because we can only exit the above loop // when writing is zero reading++ unlock(&m) // perform reading here lock(&m) reading-- cond_broadcast(&turn) unlock(&m) } writer() { lock(&m) writers++ while (reading || writing) cond_wait(&turn, &m) writing++ unlock(&m) // perform writing here lock(&m) writing-- writers-- cond_broadcast(&turn) unlock(&m) }
'Angrave System Programming > Synchronization' 카테고리의 다른 글
Synchronization: Synchronization Across Processes (0) | 2019.02.07 |
---|---|
Synchronization: Ring Buffer Example (0) | 2019.01.29 |
Synchronization: Implementing a barrier (0) | 2019.01.17 |
Synchronization: Condition Variables(2) (0) | 2019.01.16 |
Synchronization: Condition Variables(1) (1) | 2019.01.16 |