2010-05-02 3 views
2

저는 C++ boost :: thread 라이브러리를 사용하고 있습니다. 이는 필자가 pthreads를 사용하고 있음을 의미합니다. 공식적으로 뮤텍스는 잠금 장치가있는 동일한 스레드에서 잠금을 해제해야하며 한 스레드에서 잠그고 다른 스레드에서 잠금을 해제 할 수있는 효과가 필요합니다. 이것을 성취 할 수있는 방법은 여러 가지가 있습니다. 한 가지 가능성은이 동작을 허용하는 새로운 뮤텍스 클래스를 작성하는 것입니다. 또 다른 호출) (잠금을 해제하는 동안, 첫 번째 스레드가를 취득 할 수 있습니다 하나 개의 스레드() 잠금을 호출하는 경우 이후모델링 boost :: 뮤텍스가 아닌 세마포어로 잠금 가능 (이전 제목 : 다른 스레드에서 뮤텍스 잠금 해제)

class inter_thread_mutex{ 
    bool locked; 
    boost::mutex mx; 
    boost::condition_variable cv; 
public: 
    void lock(){ 
    boost::unique_lock<boost::mutex> lck(mx); 
    while(locked) cv.wait(lck); 
    locked=true; 
    } 

    void unlock(){ 
    { 
     boost::lock_guard<boost::mutex> lck(mx); 
     if(!locked) error(); 
     locked=false; 
    } 
    cv.notify_one(); 
    } 
// bool try_lock(); void error(); etc. 
} 

내가, 위의 코드는 FIFO 액세스를 보장하지 않는다는 것을 지적한다 : 예를 들어

대기중인 다른 스레드보다 먼저 잠급니다. (boost :: thread 문서는 뮤텍스 나 조건 변수에 대한 명시적인 스케쥴링 보장을하지 않는 것처럼 보입니다.) 하지만 지금은 (그리고 다른 버그들) 무시하십시오.

제 질문은 제가이 루트로 가기로 결정한 경우, 그러한 뮤텍스를 Lockable 개념의 부스트 모델로 사용할 수 있을지 의문입니다. 예를 들어 RAII 형식의 액세스에 boost::unique_lock< inter_thread_mutex >을 사용하고 boost::condition_variable_any.wait() 등으로이 잠금을 전달하면 문제가 발생합니다.

한편으로는 왜 보이지 않습니다. 반면에, "왜 안 보이지 않는가?"는 보통 무언가가 효과가 있을지 결정하는 아주 나쁜 방법입니다.

내가 묻는 이유는 RAII 잠금 및 조건 변수에 대한 래퍼 클래스를 작성해야한다는 것이 밝혀지면 다른 어떤 방법으로도 동일한 효과를 얻을 수 있습니다.

편집 : 내가 원하는 행동의 종류는 기본적으로 다음과 같습니다. 객체가 있고 수정할 때마다 잠글 필요가 있습니다. 한 스레드에서 객체를 잠그고 그 객체에 대한 작업을 수행하고 싶습니다. 그런 다음 다른 작업자 스레드가 작업을 완료하도록 지시하는 동안 객체를 잠근 상태로 유지하려고합니다. 그래서 첫 번째 쓰레드는 작업자 쓰레드가 끝나는 동안 계속 진행할 수 있습니다. 작업자 스레드가 완료되면 뮤텍스가 잠금 해제됩니다.

그리고 내가 아무렇지도 않아 스레드 1이 작업을 시작하고 스레드 2가 스레드를 완료 할 때 아무도 뮤텍스 잠금을 얻을 수 없도록 전환을 원합니다.

inter_thread_mutex와 같은 것은 작동하는 것처럼 보이며 프로그램이 일반 뮤텍스처럼 상호 작용할 수도 있습니다. 그래서 그것은 깨끗한 솔루션처럼 보입니다. 더 좋은 해결책이 있다면, 나는 또한 그것을 듣게되어 기쁩니다.

EDIT AGAIN : 잠금이 필요한 이유는 여러 개의 마스터 스레드가 있고 잠금이 잘못된 방법으로 공유 된 개체에 동시에 액세스하지 못하게하기 위해서입니다. 그래서 코드는 이미 마스터 스레드 수준에서 루프 수준의 잠금없는 시퀀싱 작업을 사용합니다. 또한 원래 구현에서는 작업자 스레드가 없었고 뮤텍스는 일반적인 코 셔 뮤텍스였습니다.

inter_thread_thingy는 주로 응답 시간을 향상시키기 위해 최적화 된 것으로 나타났습니다. 많은 경우, 작업 A의 "첫 번째 부분"이 작업 B의 "첫 번째 부분"전에 발생하는 것을 보증하는 것으로 충분했습니다. 멍청한 예를 들어, 개체 1을 펀치하고 검은 눈을 띄게한다고 말합니다. 그런 다음 대상 1에 모든 조직 손상을 반영하도록 내부 구조를 변경하라고 지시합니다. 나는 두 번째 펀치로 이동하기 전에 조직 손상을 기다리고 싶지 않습니다. 그러나 동일한 작업의 일부로 발생하는 조직 손상을 원합니다. 예를 들어, 잠시 동안 다른 스레드가 조직 손상을 잘못된 작업으로 만드는 방식으로 개체를 다시 구성하는 것을 원하지 않습니다.(예,이 예제는 여러 가지면에서 불완전합니다. 그리고 어떤 게임에서도 작동하지 않습니다.)

그래서 우리는 객체 소유권을 작업자 스레드로 전달하여 작업을 완료 할 수있는 모델을 변경했습니다 실제로는 꽤 잘 작동합니다. 각 마스터 스레드는 모두 완료 될 때까지 기다릴 필요가 없으므로 더 많은 작업을 수행 할 수 있습니다. 또한 마스터 스레드 레벨에서의 이벤트 시퀀싱은 여전히 ​​루프 기반이므로 높은 수준의 마스터 스레드 작업을 작성하는 것이 쉽습니다. 작업이 완료되었다는 가정에 기반 할 수 있기 때문입니다 (보다 정확하게는 중요한 " 첫 번째 부분 "이 해당 함수 호출이 반환 될 때 완료됩니다.

마지막으로, 모든 작업을 수행하는 데 필요한 동기화를 캡슐화하기 위해 부스트 잠금이있는 RAII를 사용하여 inter_thread 뮤텍스/세마포어를 사용하는 것이 좋을 것이라고 생각했습니다.

+0

당신의 코드는 작동하지 않습니다. 당신은 여전히 ​​정의되지 않은 동작을 일으키는 다른 스레드로부터 mx를 잠금 해제하고 있습니다. 그러나 세마포는 당신이 원하는 것을 할 것이고, 그것을 찾는다. – hirschhornsalz

+1

사실 나는 코드가 작동한다고 생각합니다. 부스트 잠금은 범위가 지정됩니다. 즉, 파괴시 뮤텍스 잠금을 해제합니다. 따라서 boost :: mutex는 잠긴 동일한 스레드에 의해 해제됩니다. 내부 뮤텍스의 목적은 잠긴 부울에 대한 액세스를 잠그는 것입니다. 그러나 내가 필요한 것은 세마포어입니다. 실제로,이 inter_thread_mutex는 사실 뮤텍스를 호출하는 바이너리 세마포어와 더 비슷합니다. – dan

+0

이 코드는이 훌륭한 SO 답변에서 링크 된 뮤텍스를 사용하여 세마포를 구현하기 위해이 코드와 비슷합니다. http://stackoverflow.com/questions/62814/바이너리 - 세마포어와 뮤텍스 간의 차이/311648 # 311648 세마포어와 뮤텍스의 관계. OP의 질문은 근본적으로, 'unique_lock' 등은 뮤텍스뿐만 아니라 세마포어와 함께 작동합니다. – Potatoswatter

답변

3

man pthread_unlock (Linux에이 OS X에 비슷한 문구) 답이 있습니다

 
NAME 
    pthread_mutex_unlock -- unlock a mutex 

SYNOPSIS 
    #include <pthread.h> 

    int 
    pthread_mutex_unlock(pthread_mutex_t *mutex); 

DESCRIPTION 
    If the current thread holds the lock on mutex, then the 
    pthread_mutex_unlock() function unlocks mutex. 

    Calling pthread_mutex_unlock() with a mutex that the 
    calling thread does not hold will result in 
    undefined behavior. 

    ... 

내 반 질문이 될 것이다 - 당신이 함께 해결하고자하는 동기화 문제의 어떤 종류의? 아마도 더 쉬운 해결책이있을 것입니다.

pthreads도 아니고 boost::thread도 (위에 구축 된) 경쟁 스레드가 경쟁 뮤텍스를 획득 한 순서를 보장하지 않습니다.

+0

나는 이것을 이해한다. 그래서 다른 스레드에서 잠금을 해제 할 수있는 새로운 뮤텍스 클래스를 작성하려고합니다. 이 질문에 제가 작성한 코드는 이것을 성취했습니다. 제 질문은 위에서 설명한 inter_thread_mutex가 다른 boost :: thread 객체, 특히 RAII 잠금 및 조건 변수와 함께 Lockable 개념으로 사용될 수 있는지 여부입니다. – dan

+0

@dan : 찾고있는 것은 뮤텍스가 아닙니다. 세마포어 또는 조건 변수와 같은 것을 찾고 있습니다. – jalf

+0

@ jalf : 예, 당신이 옳다고 생각합니다. 실제로 원하는 것은 세마포어입니다. – dan

0

죄송하지만 이해가되지 않습니다. 다른 스레드가 잠금을 해제 할 수 있다면 다음 코드에서 라인 [1]에있는 뮤텍스의 상태는 어떻게됩니까?

inter_thread_mutex m; 

{ 
    m.lock(); 
    // [1] 
    m.unlock(); 
} 

이것은 민감하지 않습니다.

+1

다른 스레드가 잠금을 해제하면 프로그램의 버그입니다. 아이디어는 한 스레드에서 뮤텍스를 잠그고 다른 스레드로 잠금을 전달한 다음 잠금을 해제하는 것입니다. – dan

+0

@dan 잠금을 해제하기 위해 다른 스레드에 잠금을 "전달하는"방법이 유용 할 수 있습니까? 그리고 정확히 "지나가는"것은 무엇을 수반합니까? –

+0

@Logan : 잠금을 "전달"하는 아이디어는 뮤텍스의 소유권이 필요한 무언가를하기 위해 두 번째 스레드에 신호를 보내는 것입니다. 두 번째 스레드가 시작되면 다시 신호를 보내고 첫 번째 스레드는 잠금을 해제하지 않고 뮤텍스의 소유권을 해제합니다. 그런 다음 두 번째 스레드가 끝나면 잠금을 해제합니다. 그러나 다른 사람들이 지적했듯이, 뮤텍스가 이것을 사용하는 것이 옳지 않은 것처럼 보입니다. – dan

-1

뮤텍스는 을 상호 배타적 인 코드 블록으로 기술하는 메커니즘입니다. 이러한 코드 블록이 스레드 경계를 넘는 것은 이해가되지 않습니다. 이와 같은 직관적 인 방식으로 그러한 개념을 사용하려고하면 문제가 생길 수 있습니다.

다른 멀티 스레딩 개념을 찾고있는 것처럼 들리지만 세부 사항이 없으면 무엇을 알기가 어렵습니다.

+0

"_A 뮤텍스는 상호 배타적 인 코드 블록을 설명하는 메커니즘입니다." "전혀 아닙니다. "상호 배타적 인 코드 블록"은 ** 중요 섹션 **의 정의입니다. 뮤텍스는 ** 데이터에 대한 독점적 액세스 **입니다. – curiousguy

+0

당신은 사소한 의미보다 우선 순위를 낮출 필요가 있다고 느꼈으므로 : "중요한 부분은 상호 배제를위한 메커니즘이나 알고리즘이 아닙니다." (http://en.wikipedia.org/wiki/Mutex). 뮤텍스는 중요한 섹션이 동시에 실행되지 않도록하는 메커니즘입니다. 거의 보편적으로 이것은 동일한 리소스에 액세스하기 때문입니다. 중요한 섹션과 상호 배제는 어쨌든 밀접하게 서로 얽힌 개념이며, 정확한 의미가 정확하게 원래의 저자가 다른 프리미티브를 찾고 있다고 느낀 점을 변경하지는 않습니다. –

0

몇 가지 방법이 있습니다. 필자가 제안하려고하는 두 가지는 객체에 추가 정보를 추가하는 것이고 오히려 소유하고있는 스레드가 아닌 다른 스레드에서 스레드의 잠금을 해제하는 메커니즘을 추가하는 것입니다.

1) 당신은 개체의 상태를 나타 내기 위해 몇 가지 정보를 추가 할 수 있습니다 : 분명히 당신이 당신의 자신의 잠금을 쓰고 있지 있기 때문에, 조건 변수와

enum modification_state { consistent, // ready to be examined or to start being modified 
          phase1_complete, // ready for the second thread to finish the work 
         }; 

// first worker thread 
lock(); 
do_init_work(object); 
object.mod_state = phase1_complete; 
unlock(); 
signal(); 
do_other_stuff(); 

// second worker thread 
lock() 
while(object.mod_state != phase1_complete) 
    wait() 
do_final_work(obj) 
object.mod_state = consistent; 
unlock() 
signal() 

// some other thread that needs to read the data 
lock() 
while(object.mod_state != consistent) 
    wait(); 
read_data(obj) 
unlock() 

작품 잘합니다.

2) 특정 스레드가 있다는 것을 염두에두면 개체 소유자에게 줄 수 있습니다.

// first worker 
    lock(); 
    while(obj.owner != this_thread()) wait(); 
    do_initial_work(obj); 
    obj.owner = second_thread_id; 
    unlock() 
    signal() 

    ... 

이 거의 동일한 내 첫 번째 솔루션으로 해결하지만, 더 유연하다/추가 단계로 제거하고, 추가/스레드의 제거에 덜 유연.

솔직하게 말하면, 인터 스레드 뮤텍스가 어떻게 도움이 될지 모르겠습니다. 두 번째 스레드로 작업이 전달되었음을 알리기 위해 여전히 세마포 또는 조건 변수가 필요합니다.

+0

감사합니다. 현재 사용하고있는 구현은 위의 (1)과 비슷합니다. 나는 inter_thread_mutex와 같은 것으로 간소화 될 수도 있다고 생각했다. 그러나 그 대답은 아마도 아니오 일 것 같습니다. 실제로 inter_thread_mutex 클래스는 뮤텍스보다 바이너리 세마포어에 더 가깝습니다. 또한 시그널링을 위해 조건 변수를 사용하고 있으므로 CVS를 피하려고하지는 않습니다. 한 스레드에서 다른 스레드로 "잠긴 리소스의 소유권 전달"이라는 아이디어를 모델링하는 깨끗한 방법을 찾고 있습니다. – dan

0

당신이 이미 가지고있는 것의 작은 수정 : 당신은 을 원하는 스레드의 ID를 저장하는 것이 어떻습니까? inter_thread_whatever에서 잠금을 취하기 위해이 필요합니까? 그런 다음 잠금을 해제하고 해당 스레드에 메시지를 보내면서 "이 잠금을 유지하려고하는 루틴이 무엇이든 실행하십시오."라고 말합니다.

lock의 조건은 while(locked || (desired_locker != thisthread && desired_locker != 0))이됩니다. 기술적으로는 첫 번째 스레드에서 "잠금 해제"하고 두 번째 스레드에서는 "다시 가져 왔습니다"하지만 다른 스레드가 그 스레드를 잡을 수는 없습니다. 하나씩.

스레드가 종료되거나 죽은 경우 잠금 장치가 원하는 잠금 장치 인 동안 해당 스레드가 교착 상태에 빠질 수있는 문제가 있습니다. 그러나 이미 두 번째 스레드가 잠금을 성공적으로 획득했다는 메시지를 기다리는 첫 번째 스레드에 대해 이미 이야기하고 있으므로 아마도 메시지가 수신되지 않으면 어떤 일이 벌어 질지 이미 염두에 두었을 것입니다. 그 계획에 "inter_thread_whatever에 desired_locker 필드 재설정"을 추가하십시오.

이것은 모두 매우 털이 있지만, 제가 제안한 것이 맞다는 것을 확신하지 못합니다. "마스터"스레드 (이러한 모든 도우미를 지시하는 스레드)는 첫 번째 op가 완료 될 때까지이 잠금에 의해 보호되는 모든 작업을 더 이상 수행하지 않도록합니다. 또는 실패하고 일부 RAII 것은 당신에게 알려줍니다)? 메시지 루프 수준에서 처리 할 수 ​​있다면 잠금 장치가 필요하지 않습니다.

+0

의견에 감사드립니다. (1) 스레드 ID를 저장하는 방법에 대해 생각했지만 소유권을 논리적으로 관리하는 것이 더 깔끔한 것이라고 생각했습니다. 생각한 실험에서 스레드 ID를 명시 적으로 사용하면 결국 더 복잡한 문제가 발생하게됩니다 (예 : 토론 할 때 오류 처리 중). (2) 예, 답을 결코받지 못하는 문제가 있습니다. 간단히 말해서이 경우 "nack"또는 ack-fail 메시지가 전송되면 잠금을 복구하고 해제합니다. – dan

+0

... (3) 마스터 스레드 잠금 기능이 전반적으로 어떻게 작동하는지에 대한 자세한 설명을 위해 원본 메시지를 편집했습니다. – dan

0

inter_thread_mutex (binary_semaphore)이 Lockable의 모델로 볼 수 있다고 나는 생각하지 않습니다. 주요 문제는 inter_thread_mutex의 주요 기능이 Locakble 개념을 상실한다는 것입니다. inter_thread_mutex이 잠글 수있는 모델 인 경우 In [1]에서 inter_thread_mutexm이 잠겨 있다고 예상 할 수 있습니다.

// thread T1 
inter_thread_mutex m; 

{ 
    unique_lock<inter_thread_mutex> lk(m); 
    // [1] 
} 

그러나 T1 [1]은 담보가 고장 인 동안 m.unlock() 할 수있는 다른 스레드 T2로서

.

잠금 해제 전에 각 스레드가 잠금을 시도하는 한 이진 세마포는 Lockables으로 사용할 수 있습니다. 그러나 당신 반의 주된 목표는 정반대입니다.

이것은 Boost.Interprocess의 세마포어가 잠금/잠금 해제 기능을 사용하여 함수의 이름을 지정하지만 대기/알림을 사용하지 않는 이유 중 하나입니다. 이상하게도 조건에 의해 사용되는 같은 이름입니다.

+0

"In_ [1]에서'inter_thread_mutex''m이 잠겨 있음을 기대할 것입니다.''lk'을 다른 스레드로 넘겨주지 않으면! 나는 여기서 문제가 무엇인지 보지 못한다. 디자인은 간단하고, 정확하고, 명백하며, 대안 솔루션은 아마도 훨씬 더 복잡 할 것입니다. – curiousguy

관련 문제