Difference in Concurrency Model in MacOS X and MS Windows (4)

요새 지구촌 공생회일과 관련해서 돕고 있는 일이 있어서, 한 1주일간 포스팅을 제대로 못했다. 비록 이 블로그는 나의 생각을 정리하는 것이 주 목표이고, 언제든지 글을 쓸때의 마음으로 돌아가서 다시 리뷰해보고, 재빠르게 해당 주제로 내 생각을 context-switching하기 위한 것이어서, 내 마음 내키는 때에 글을 올리면 되겠지만, 이제 각종 검색 엔진에 이 블로그를 공개했고, 찾아오는 이들도 내 생각보다는 꽤 되어서, 알게 모르게 어떤 “의무감”을 느끼게 되다보니, 하나의 “숙제”가 되어가는 느낌이다. 앞으로 OpenMP나 aligned malloc에 대한 구현에 대한 글도 쓰려고 하는데, (한글로는 아니고..) 우선 이 synchronization에 대한 글을 마무리져야겠다는 느낌이다.

I have not posted for last week, due to my involvement to the Good Hands for Globe”. Although the purpose of this blog is to organize my thought, and to return the state of my mind when I post an article anytime, or in other words to contex-switch quickly to subjects in posts, it became like “homework” after I made this blog public to a few search engines. Indeed, not a few people visit this blog.
By the way, I would like to finish this “synchronization” post for new ones like OpenMP and implementation of aligned malloc.

  • pthread (POSIX thread )

Mac OS X에서의 synchronization을 이야기 하기 전에, 우선 pthread에 대한 간략한 언급이 먼저 있어야겠다. 이 pthread는 Unix 환경에서의 사실상 표준인 것이기 때문이다. 또한 BSD를 그 모태로 하는 Mac OS X는 그 기저에서 이런 pthread를 사용한다. Objective-C와 Cocoa는 그런 모태를 제쳐두고는 이야기할 수가 없다.

Before talking about the synchronization for Mac OS X, pthread should be mentioned at least. The pthread is the de-facto standard of threading on Unix environment. Moreover, the Mac OS X uses this pthread in its lower base, because it is based on the BSD. Therefore withouth the pthread, there can’t be the synchronization model of Objective-C and Cocoa.

pthread에 대한 좋은 tutorial은 다음의 두 웹 사이트에 잘 나와 있다.
There are good tutorials on the pthread as follows:

POSIX Threads Programming

Linux Tutorial : POSIX Threads Libraries

위 사이트에서 나온 예를 잠깐 보자.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *functionC();
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int  counter = 0;

main()
{
   int rc1, rc2;
   pthread_t thread1, thread2;

   /* Create independent threads each of which will execute functionC */

   if( (rc1=pthread_create( &thread1, NULL, &functionC, NULL)) )
   {
      printf("Thread creation failed: %d\n", rc1);
   }

   if( (rc2=pthread_create( &thread2, NULL, &functionC, NULL)) )
   {
      printf("Thread creation failed: %d\n", rc2);
   }

   /* Wait till threads are complete before main continues. Unless we  */
   /* wait we run the risk of executing an exit which will terminate   */
   /* the process and all threads before the threads have completed.   */

   pthread_join( thread1, NULL);
   pthread_join( thread2, NULL); 

   exit(0);
}

void *functionC()
{
   pthread_mutex_lock( &mutex1 );
   counter++;
   printf("Counter value: %d\n",counter);
   pthread_mutex_unlock( &mutex1 );
}

functionC()를 보면 mutex를 어떻게 thread화 된 함수에서 사용하는지가 간략하게 나와 있다. pthread는 모델이 무척 간결하여, 이 예만으로도 거의 대부분을 파악할 수가 있다. 아무튼 이 함수를 보면 mutex1이라는 mutex 변수에 대해서 lock을 걸고, 뭔가를 한 후에 lock을 푸는 것을 볼 수있다. Windows의 경우와 같이 WaitForSingleObject()와 같은 별도의 함수를 사용하지 않는다. 앞의 글에서 언급되었듯이, 차라리 MFC의 모델과 닮지 않았는가? 아마도 MFC의 그것은 이런 산업 표준의 thread를 사용하는 개발자들이 쉽게 접근할 수있도록 디자인된 것 같다.

functionC() shows how to use the mutex variables in a threaded function. The model of pthread is so simple that it is possible to understand how the pthrea functions and data are organized and how to use them. Anyway, it locks the mutex variable, mutex1, and release it using unlock(). It doesn’t use a function like, WaitForSingleObject() which is for the Win32. This is very similar to the model for the MFC. Probably the MFC was designed such that programmers familiar with the industry standard could learn the threading using MFC easier.

그럼 critical section은 어떨까? pthread에선 위의 두 웹 페이지에 나와 있듯이 따로 “CRITICAL_SECTION”과 같이 준비된 타입의 변수형은 없다. mutex와 같은 synchronization variable에 의해 regulate되는 블럭을 원래 critical section이라고 부른다. 사실 나로써는 왜 MS가 따로 CRITICAL_SECTION을 만들었는지 개념적으로 잘 이해가 가지 않는다. 물론 MSDN 설명서에 나와 있듯이, single machine, single process 내에서 속도가 더 빠르다고 하니, 아마 그런 optimization을 하기 위해서 특별히 만들어낸게 아닐까 한다.

Then, what about the critical section? If you search the two web site, you will not be able to find any special data type like “CRITICAL_SECTION”. The original concept of critical section is a code block surbodinated and protected by lock() and unlock() functions on a synchronization variable. So, you don’t need such a special data type. Conceptually it is somewhat difficult to understand why MS made such a special type. As its MSDN explains, the CRITICAL_SECTION is faster on a single machine, a single process. So, to optimize more in such case, I guess MS made it.

그런데 이런 pthread가 WIN32처럼 기본적으로 쓰이는 API를 위한 framework인 MFC와 같은 수준의 추상화를 해 준다는 점이 재미있다.

It is interesting that the pthread has the same level of abstraction to the MFC which is a framework for the Win32 API.

  • Synchronization in Objective-C

Objective-C는 언어 자체에서 synchronizatin을 지원한다. 즉 @synchronized()란 것을 지원하는데, 이것을 GCC 3.3과 그 이후의 버젼에서 지원받으려면 -fobjc-exceptions란 gcc 패러미터를 넣어 주어야 한다.

The support to synchronization is built in the Objective-C. To use the feature, a parameter -fobjc-exceptions should be passed to the gcc. Then, it can use @synchonized().

자 사용예를 한번 보자.

Let’s take a look at some examples.

- (void)criticalMethod
{
    @synchronized(self) {

        // Critical code.
        ...

    }
}

혹은 현재의 selector를 mutex로 사용할 수도 있다.

Or, the current selector, i.e. method, can be used as a mutex.

- (void)criticalMethod
{
    @synchronized(NSStringFromSelector(_cmd)) {

        // Critical code.
        ...

    }
}

굉장히 간결하지 않은가? 이것은 WIN32의 CRITICAL_SECTION보다 더 간결하다.
Isn’t it very simple? This is even simpler than the CRITICAL_SECTION of the Win32.

  • Synchronization in Cocoa

Lock을 사용하는 기본 예제를 보자.
Let’s take a look at a basic example.

BOOL moreToDo = YES;

NSLock *theLock = [[NSLock alloc] init];
...

while (moreToDo) {
    /* Do another increment of calculation */

    /* until there’s no more to do. */

    if ([theLock tryLock]) {

        /* Update display used by all threads. */

        [theLock unlock];
    }
}

tryLock을 사용한 부분은 그냥 lock을 사용한 것과 개념적으론 같다. 아무튼 이 예제에서 알 수있는 바와 같이, pthread의 그것을 그대로 닯았다. Cocoa는 그 기저에 BSD function들이 있다. 그러므로 그 함수들이 제시하는 프로그래밍 모델을 자연스럽게 따를 수밖에 없는 것이다.

The tryLock is conceptually analoguous to lock() of the pThread. Anyway, it resembles that of pthread. Cocoa is a kind of wrapper to its base architecture. So, it naturally follows the model of BSD functions and pthread.

여기에 추가적으로 Cocoa는 다양한 Lock을 제공한다. 이 Lock들은 근본적으로 다른 Lock이 아니라, 기본 lock을 어떻게 사용하는가에 따라 카테고리를 주어서 만든것이다. 이런 Lock의 종류로는 Mutex, Recursive Lock, Read-Write Lock, Distributed Lock, Spin Lock 이 있고, NSCondition 클래스를 이용한 condition lock이 있다.
엄밀하게 말해서 mutex 자체는 lock이 아니다. lock 관련 클래스들이 이런 mutex와 같은 semaphore value에 대해서 lock을 거는 것이다. mutex를 여기에 놓아서 문서를 읽는 이로 하여금 헷갈리게 만들었다.

In addition tho that, the Cocoa provide variaous lock types. They are basically same, but categorized by its use. They are mutex, Recursive Lock, Read-Write Lock, Distributed Lock and Spin Lock. Also there is a condition lock using NSCondition class.
The mutex is not lock. The various lock class uses the mutex for locking. By enlisting the mutex here, Apple confused people.

사용예는 다음의 문서를 참조하기 바란다.
About how to use them, please read this documentation.

http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/chapter_5_section_7.html#//apple_ref/doc/uid/10000057i-CH8-SW17

pthread도 condition lock이 있고, Cocoa가 제공하는 다양한 lock의 형태는 pthread의 기본 lock을 이용해서 구현할 수가 있다. 그러므로 전반적으로 봤을때, pthread의 synchronization model과 같음을 알 수있다.

pthread has condition lock and all kinds of lock in Cocoa can be implemented using the basic lock of the pthread. So, the synchronization model of the Mac is same to that of the pthread.

실제로, 굳이 pthread를 사용해도 된다.
Actually you can replace them with those from pthread.

여기서 재미난 점은 Spin Lock이다. 제일 흔한 lock인데, 이건 해당 mutex에 대한 lock을 얻을 수있는지 아닌지 계속 polling을 하면서 block되는 것이다. 이건 WaitForSingleObject()에서 그 첫째 인자로 INFINITE를 전달한 것과 같다. NSLock같은 경우엔 tryLock을 호출함으로써, lock을 얻을 수없으면 바로 해당 block을 pass할 수있는 유연성을 준다. WaitForSingleObject()의 경우엔 그 두번째 인자에 0을 전달하면 같은 효과를 볼 수있다.

What is interesting is Spin Lock. It is the most common lock, and keeps polling to acquire a lock. Thus it is blocked. This is similar to the WaitForSingleObject() with INFINITE as its 1st parameter.
NSLock provides flexibility by calling tryLock. It is not blocked. For the case of WaitForSingleObject(), you can pass 0 to its 2nd parameter.

여기서 알수있듯이 Cocoa/Unix의 경우는 mutex (혹은 semaphore) 와 lock의 두 개념만으로 synchronization을 수행한다. 반면에 Win32는 WaitForSingleObject()같은 별도의 함수를 이용한다. 사실 그 함수가 lock()과 마찬가지인데, 왜 굳이 보통 쓰이는 lock()을 안쓰고 새롭게 이름을 지었는지 모르겠다. 물론 MFC에서 해결을 해주곤 있지만..

For now, you can see that only two concepts, mutex (or semaphore ) and lock are necessary for the Cocoa and Unix, while there is a separate function, WaitForSingleObject() for the Win32. Actually it is analogous to the lock(). I don’t understand why MS invented a new name for that, although MFC solves the issue.

(C++/C#/CLI의 경우는 Cococa와 비슷하다는 점을 언급해두고 싶다.)
(I would like to mention C++/C#/CLI case is similar to the Cocoa.)

이상으로 각 플랫폼에서의 synchronization 모델에 대해서 살펴보았다. 이렇게 놓고 보면 다 매우 흡사해 보인다. 하지만 처음 MSDN 문서와 Apple 그리고 pthread 문서를 놓고 비교해 가면서 각 플랫폼에서 코드를 만드는 사람들은 헷갈릴 수가 있다. 이 문서가 그런 분들에게 도움이 되었으면 좋겠다.

So far, we figured out what the synchronization models on various platform look like. At this point, they look all the same, or similar. However, people who are fist to the platforms can be confused. So, I hope thes posts could help them.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: