뮤텍스 예제 / 튜토리얼?
멀티 스레딩을 처음 접했고 뮤텍스의 작동 방식을 이해하려고했습니다. 많은 인터넷 검색을 했는데 괜찮은 튜토리얼을 찾았 지만 잠금이 작동하지 않는 자체 프로그램을 만들었으므로 여전히 작동 방식에 대한 의구심을 남겼습니다.
뮤텍스의 직관적이지 않은 구문은 뮤텍스가 pthread_mutex_lock( &mutex1 );
잠겨있는 것처럼 보입니다. 실제로 잠그고 싶은 것이 다른 변수 일 때. 이 구문은 뮤텍스를 잠그면 뮤텍스가 잠금 해제 될 때까지 코드 영역을 잠그는 것을 의미합니까? 그렇다면 스레드가 영역이 잠겨 있음을 어떻게 알 수 있습니까? [ 업데이트 : 스레드는 메모리 펜싱에 의해 영역이 잠겨 있음을 알고 있습니다 ]. 그리고 그러한 현상을 비판적 섹션이라고 부르지 않습니까? [ 업데이트 : 중요 섹션 객체는 Windows에서만 사용할 수 있으며, 객체는 뮤텍스보다 빠르며이를 구현하는 스레드에서만 볼 수 있습니다. 그렇지 않으면, 임계 섹션은 단지 뮤텍스에 의해 보호되는 코드 영역을 참조합니다 ]
요컨대, 가장 간단한 mutex 예제 프로그램 과 그 작동 방식의 논리에 대한 가장 간단한 설명 을 도와 주실 수 있습니까? 나는 이것이 다른 많은 초보자들에게 도움이 될 것이라고 확신합니다 .
전 세계의 초보자에게 개념을 설명하려는 겸손한 시도가 있습니다. ( 내 블로그 의 색상 코드 버전 )
많은 사람들이 고독한 전화 부스 (모바일 없음)로 달려가 사랑하는 사람과 대화합니다. 부스의 문 손잡이를 잡은 첫 번째 사람은 전화를 사용할 수있는 사람입니다. 그는 전화를 사용하는 한 문 손잡이를 계속 잡고 있어야합니다. 그렇지 않으면 다른 사람이 손잡이를 잡고 밖으로 내던져 아내와 대화를 나눌 것입니다. :) 대기열 시스템이 없습니다. 상대방이 전화를 마치고 부스에서 나와 도어 핸들을 나가면 도어 핸들을 잡을 다음 사람이 전화를 사용할 수 있습니다.
스레드 입니다 : 각 사람 뮤텍스 이다 : 문이 처리 잠금이 사람의 손이 :이다 자원이 있다 : 전화
전화를 사용하여 아내와 대화하기 위해 다른 스레드에서 동시에 수정해서는 안되는 코드 줄을 실행 해야하는 스레드는 먼저 뮤텍스에 대한 잠금을 획득해야합니다 (부스의 도어 핸들 클러치) ). 그래야만 스레드가 해당 코드 줄을 실행할 수 있습니다 (전화 걸기).
스레드가 해당 코드를 실행하면 다른 스레드가 뮤텍스에 대한 잠금을 획득 할 수 있도록 뮤텍스에 대한 잠금을 해제해야합니다 (다른 사람들이 전화 부스에 액세스 할 수 있음).
[ 뮤텍스를 갖는 개념은 실제 배타적 액세스를 고려할 때 약간 불합리하지만 프로그래밍 세계에서는 다른 스레드가 스레드가 이미 일부 코드 라인을 실행하고 있다고 볼 수있는 다른 방법이 없다고 생각합니다. 재귀 뮤텍스 등의 개념이 있지만이 예제는 기본 개념을 보여주기위한 것입니다. 예제가 개념의 명확한 그림을 제공하기를 바랍니다. ]
C ++ 11 스레딩의 경우 :
#include <iostream>
#include <thread>
#include <mutex>
std::mutex m;//you can use std::lock_guard if you want to be exception safe
int i = 0;
void makeACallFromPhoneBooth()
{
m.lock();//man gets a hold of the phone booth door and locks it. The other men wait outside
//man happily talks to his wife from now....
std::cout << i << " Hello Wife" << std::endl;
i++;//no other thread can access variable i until m.unlock() is called
//...until now, with no interruption from other men
m.unlock();//man lets go of the door handle and unlocks the door
}
int main()
{
//This is the main crowd of people uninterested in making a phone call
//man1 leaves the crowd to go to the phone booth
std::thread man1(makeACallFromPhoneBooth);
//Although man2 appears to start second, there's a good chance he might
//reach the phone booth before man1
std::thread man2(makeACallFromPhoneBooth);
//And hey, man3 also joined the race to the booth
std::thread man3(makeACallFromPhoneBooth);
man1.join();//man1 finished his phone call and joins the crowd
man2.join();//man2 finished his phone call and joins the crowd
man3.join();//man3 finished his phone call and joins the crowd
return 0;
}
다음을 사용하여 컴파일 및 실행 g++ -std=c++0x -pthread -o thread thread.cpp;./thread
대신 명시 적으로 사용 lock
하고 unlock
, 당신은 괄호를 사용하여 다음과 같이 당신이 범위의 잠금 사용하는 경우, 이 제공하는 이점에 대한을 . 범위가 지정된 잠금은 약간의 성능 오버 헤드가 있습니다.
TBB 사용 : 아래 프로그램을 실행 하려면 TBB 가 필요 하지만 TBB 코드를 게시하려는 목적은 간단한 코드 만보고 잠금 및 잠금 해제 순서를 이해하는 것입니다. 릴리스- 예외 안전 -그러나 이것은 더 명확합니다).
#include <iostream>
#include "/tbb/mutex.h"
#include "/tbb/tbb_thread.h"
using namespace tbb;
typedef mutex myMutex;
static myMutex sm;
int i = 0;
void someFunction()
{
//Note: Since a scoped lock is used below, you should know that you
//can specify a scope for the mutex using curly brackets, instead of
//using lock.acquire() and lock.release(). The lock will automatically
//get released when program control goes beyond the scope.
myMutex::scoped_lock lock;//create a lock
lock.acquire(sm);//Method acquire waits until it can acquire a lock on the mutex
//***only one thread can access the lines from here...***
++i;//incrementing i is safe (only one thread can execute the code in this scope) because the mutex locked above protects all lines of code until the lock release.
sleep(1);//simply creating a delay to show that no other thread can increment i until release() is executed
std::cout<<"In someFunction "<<i<<"\n";
//***...to here***
lock.release();//releases the lock (duh!)
}
int main()
{
tbb_thread my_thread1(someFunction);//create a thread which executes 'someFunction'
tbb_thread my_thread2(someFunction);
tbb_thread my_thread3(someFunction);
my_thread1.join();//This command causes the main thread (which is the 'calling-thread' in this case) to wait until thread1 completes its task.
my_thread2.join();
my_thread3.join();
}
참고 tbb_thread.h
되지 않습니다. 교체는 여기 에 표시 됩니다 .
뮤텍스가 다른 문제를 해결하는 데 사용될 수 있지만 이들이 존재하는 주된 이유는 상호 배제를 제공하여 경쟁 조건으로 알려진 것을 해결하는 것입니다. 두 개 이상의 스레드 또는 프로세스가 동일한 변수에 동시에 액세스하려고하면 경쟁 조건이 발생할 수 있습니다. 다음 코드를 고려하십시오
//somewhere long ago, we have i declared as int
void my_concurrently_called_function()
{
i++;
}
이 함수의 내부는 매우 단순 해 보입니다. 하나의 진술 일뿐입니다. 그러나 일반적인 의사 어셈블리 언어는 다음과 같습니다.
load i from memory into a register
add 1 to i
store i back into memory
i에서 증분 작업을 수행하려면 동등한 어셈블리 언어 명령어가 모두 필요하므로 i를 증분하는 것은 비대기 작업이라고합니다. 원 자성 작업은 명령 실행이 시작된 후에 중단되지 않는 보증으로 하드웨어에서 완료 될 수있는 작업입니다. i를 증가 시키면 3 개의 원자 명령어 체인으로 구성됩니다. 여러 스레드가 함수를 호출하는 동시 시스템에서 스레드가 잘못된 시간에 읽거나 쓸 때 문제가 발생합니다. 동시 실행을 실행하는 두 개의 스레드가 있고 하나는 즉시 다른 함수를 호출한다고 상상해보십시오. 또한 0으로 초기화했다고 가정 해 봅시다. 또한 레지스터가 많고 두 스레드가 완전히 다른 레지스터를 사용한다고 가정하므로 충돌이 발생하지 않습니다. 이러한 이벤트의 실제 타이밍은 다음과 같습니다.
thread 1 load 0 into register from memory corresponding to i //register is currently 0
thread 1 add 1 to a register //register is now 1, but not memory is 0
thread 2 load 0 into register from memory corresponding to i
thread 2 add 1 to a register //register is now 1, but not memory is 0
thread 1 write register to memory //memory is now 1
thread 2 write register to memory //memory is now 1
일어난 일은 동시에 두 개의 스레드가 증가하고 함수가 두 번 호출되지만 결과는 그 사실과 일치하지 않습니다. 함수가 한 번만 호출 된 것 같습니다. 이는 기계 수준에서 원 자성이 "파손"되었기 때문에 스레드가 서로 방해하거나 잘못된 시간에 함께 작동 할 수 있습니다.
이를 해결하기위한 메커니즘이 필요합니다. 위의 지침에 따라 주문을해야합니다. 하나의 일반적인 메커니즘은 하나를 제외한 모든 스레드를 차단하는 것입니다. Pthread mutex는이 메커니즘을 사용합니다.
전화로 아내와 대화하기 위해 다른 스레드가 공유 값을 안전하지 않게 수정할 수있는 일부 코드 행을 실행해야하는 스레드는 먼저 뮤텍스에 대한 잠금을 획득해야합니다. 이러한 방식으로 공유 데이터에 액세스해야하는 모든 스레드는 뮤텍스 잠금을 통과해야합니다. 그래야만 스레드가 코드를 실행할 수 있습니다. 이 코드 섹션을 중요 섹션이라고합니다.
스레드가 중요한 섹션을 실행하면 다른 스레드가 뮤텍스에 대한 잠금을 획득 할 수 있도록 뮤텍스에 대한 잠금을 해제해야합니다.
The concept of having a mutex seems a bit odd when considering humans seeking exclusive access to real, physical objects but when programming, we must be intentional. Concurrent threads and processes don't have the social and cultural upbringing that we do, so we must force them to share data nicely.
So technically speaking, how does a mutex work? Doesn't it suffer from the same race conditions that we mentioned earlier? Isn't pthread_mutex_lock() a bit more complex that a simple increment of a variable?
Technically speaking, we need some hardware support to help us out. The hardware designers give us machine instructions that do more than one thing but are guranteed to be atomic. A classic example of such an instruction is the test-and-set (TAS). When trying to acquire a lock on a resource, we might use the TAS might check to see if a value in memory is 0. If it is, that would be our signal that the resource is in use and we do nothing (or more accurately, we wait by some mechanism. A pthreads mutex will put us into a special queue in the operating system and will notify us when the resource becomes available. Dumber systems may require us to do a tight spin loop, testing the condition over and over). If the value in memory is not 0, the TAS sets the location to something other than 0 without using any other instructions. It's like combining two assembly instructions into 1 to give us atomicity. Thus, testing and changing the value (if changing is appropriate) cannot be interrupted once it has begun. We can build mutexes on top of such an instruction.
Note: some sections may appear similar to an earlier answer. I accepted his invite to edit, he preferred the original way it was, so I'm keeping what I had which is infused with a little bit of his verbiage.
The best threads tutorial I know of is here:
https://computing.llnl.gov/tutorials/pthreads/
I like that it's written about the API, rather than about a particular implementation, and it gives some nice simple examples to help you understand synchronization.
I stumbled upon this post recently and think that it needs an updated solution for the standard library's c++11 mutex (namely std::mutex).
I've pasted some code below (my first steps with a mutex - I learned concurrency on win32 with HANDLE, SetEvent, WaitForMultipleObjects etc).
Since it's my first attempt with std::mutex and friends, I'd love to see comments, suggestions and improvements!
#include <condition_variable>
#include <mutex>
#include <algorithm>
#include <thread>
#include <queue>
#include <chrono>
#include <iostream>
int _tmain(int argc, _TCHAR* argv[])
{
// these vars are shared among the following threads
std::queue<unsigned int> nNumbers;
std::mutex mtxQueue;
std::condition_variable cvQueue;
bool m_bQueueLocked = false;
std::mutex mtxQuit;
std::condition_variable cvQuit;
bool m_bQuit = false;
std::thread thrQuit(
[&]()
{
using namespace std;
this_thread::sleep_for(chrono::seconds(5));
// set event by setting the bool variable to true
// then notifying via the condition variable
m_bQuit = true;
cvQuit.notify_all();
}
);
std::thread thrProducer(
[&]()
{
using namespace std;
int nNum = 13;
unique_lock<mutex> lock( mtxQuit );
while ( ! m_bQuit )
{
while( cvQuit.wait_for( lock, chrono::milliseconds(75) ) == cv_status::timeout )
{
nNum = nNum + 13 / 2;
unique_lock<mutex> qLock(mtxQueue);
cout << "Produced: " << nNum << "\n";
nNumbers.push( nNum );
}
}
}
);
std::thread thrConsumer(
[&]()
{
using namespace std;
unique_lock<mutex> lock(mtxQuit);
while( cvQuit.wait_for(lock, chrono::milliseconds(150)) == cv_status::timeout )
{
unique_lock<mutex> qLock(mtxQueue);
if( nNumbers.size() > 0 )
{
cout << "Consumed: " << nNumbers.front() << "\n";
nNumbers.pop();
}
}
}
);
thrQuit.join();
thrProducer.join();
thrConsumer.join();
return 0;
}
The function pthread_mutex_lock()
either acquires the mutex for the calling thread or blocks the thread until the mutex can be acquired. The related pthread_mutex_unlock()
releases the mutex.
Think of the mutex as a queue; every thread that attempts to acquire the mutex will be placed on the end of the queue. When a thread releases the mutex, the next thread in the queue comes off and is now running.
A critical section refers to a region of code where non-determinism is possible. Often this because multiple threads are attempting to access a shared variable. The critical section is not safe until some sort of synchronization is in place. A mutex lock is one form of synchronization.
You are supposed to check the mutex variable before using the area protected by the mutex. So your pthread_mutex_lock() could (depending on implementation) wait until mutex1 is released or return a value indicating that the lock could not be obtained if someone else has already locked it.
Mutex is really just a simplified semaphore. If you read about them and understand them, you understand mutexes. There are several questions regarding mutexes and semaphores in SO. Difference between binary semaphore and mutex, When should we use mutex and when should we use semaphore and so on. The toilet example in the first link is about as good an example as one can think of. All code does is to check if the key is available and if it is, reserves it. Notice that you don't really reserve the toilet itself, but the key.
SEMAPHORE EXAMPLE ::
sem_t m;
sem_init(&m, 0, 0); // initialize semaphore to 0
sem_wait(&m);
// critical section here
sem_post(&m);
Reference : http://pages.cs.wisc.edu/~remzi/Classes/537/Fall2008/Notes/threads-semaphores.txt
For those looking for the shortex mutex example:
#include <mutex>
using namespace std;
int main() {
mutex m;
m.lock();
// do thread-safe stuff
m.unlock();
return 0;
}
참고URL : https://stackoverflow.com/questions/4989451/mutex-example-tutorial
'IT' 카테고리의 다른 글
정적과 최종의 차이점은 무엇입니까? (0) | 2020.06.01 |
---|---|
Javascript date.getYear ()는 2011에서 111을 반환합니까? (0) | 2020.06.01 |
IntPtr은 정확히 무엇입니까? (0) | 2020.06.01 |
명명 규칙 : "상태"대 "상태" (0) | 2020.06.01 |
Moq를 사용하여 단위 테스트를위한 비동기 메소드 조롱 (0) | 2020.06.01 |