'Common C#/C# 기본 문법 연구'에 해당되는 글 12건

  1. 2009.03.03 C++ template 와 C# Generic 차이점
  2. 2009.03.03 형식 매겨 변수 #3
  3. 2009.03.03 형식 매개변수 #2
  4. 2009.03.03 형식 매개 변수에 대한 제약 조건
  5. 2009.01.23 Delegate 비동기 #2
  6. 2009.01.23 Delegate 비동기화 사용
  7. 2008.11.14 Thread #1
  8. 2008.11.14 Thread #2_1
  9. 2008.11.14 Thread #2
  10. 2008.11.14 Thread #3

http://msdn2.microsoft.com/ko-kr/library/c6cyy67b(VS.80).aspx 참조

 - C++에서는 템플릿의 모든 형식 매개 변수에 대해서는 유효하지 않을 수 있는 코드가 허용되며 이러한 코드는 형식 매개 변수로 사용되는 특정 형식에 대해 검사를 받습니다. C#에서는 제약 조건을 충족하는 모든 형식과 함께 사용할 수 있도록 클래스의 코드를 작성해야 합니다. 예를 들어, C++의 경우 형식 매개 변수의 개체에 대해 + 및 - 산술 연산자를 사용하는 함수를 작성할 수 있습니다. 이 경우 이러한 연산자를 지원하지 않는 형식으로 템플릿을 인스턴스화할 때 오류가 발생합니다. C#에서는 이러한 함수를 작성할 수 없고 제약 조건에서 추론 가능한 언어 구문만 사용할 수 있습니다.

코드로 보면

[C++]
template<class T>
class WidgetT
{
    void DoSomething(T t)
    {
        t.Process();
    }

};

위와 같은 구현이 가능했지만

[C#]
class WidgetT<T>
{
    public void DoSomething(T t)
    {
        t.Process();
    }
};

이렇게 구현하면 컴파일 에러가 납니다.
class T가 Process()를 구현하는지 알수없기 때문에 허용해 줄 수 없다는 이야기 입니다.
하지만 T가 Process()를 구현하는 것을 확인해주면 허용해 줄 수 있다는 말도 되겠지요.

[C#]
class WidgetT<T> where T : ISomeInterface
{
    public void DoSomething(T t)
    {
        t.Process();
    }
};

interface ISomeInterface
{
    void Process();
};

이렇게 정의하고 실제 구현부분에서는

class SomeHandler : ISomeInterface
{
    public void Process()
    {
        // 구현
    }
};


SomeHandler handler = new SomeHandler();
WidGet<SomeHandler> wg = new WidGet<SomeHandler>();
wg.DoSomething(handler);

이렇게 사용하시면 됩니다.

제네릭의 형식매개변수에 대한  좀더 자세한 설명은 http://msdn2.microsoft.com/ko-kr/library/d5x73970(VS.80).aspx 에 있습니다.

[출처] C++ 템플릿과 C# 제네릭의 차이점 #2|작성자 자바워크

Posted by penguindori

형식 매개 변수를 제한하면, 허용되는 작업의 수 및 제한하는 형식과 해당 상속 계층 구조의 모든 형식에서 지원하는 메서드에 대해 허용되는 호출의 수를 늘릴 수 있습니다. 따라서 제네릭 클래스나 메서드를 디자인할 때 System.Object에서 지원하지 않는 메서드를 호출하거나 제네릭 멤버에 대해 단순한 할당 이상의 작업을 수행하려는 경우에는 형식 매개 변수에 제약 조건을 적용해야 합니다.

where T : class 제약 조건을 적용할 때는 형식 매개 변수에 대해 == 및 != 연산자를 사용하지 않는 것이 좋습니다. 이러한 연산자에서는 값이 같은지 확인하는 대신 참조가 동일한지만 테스트하기 때문입니다. 인수로 사용되는 형식에서 이러한 연산자를 오버로드한 경우에도 마찬가지입니다. 다음 코드에서는 이러한 경우의 예를 보여 줍니다. String 클래스에서 == 연산자를 오버로드해도 false가 출력됩니다.

C# 코드 복사
public static void OpTest<T>(T s, T t) where T : class
{
    System.Console.WriteLine(s == t);
}
static void Main()
{
    string s1 = "foo";
    System.Text.StringBuilder sb = new System.Text.StringBuilder("foo");
    string s2 = sb.ToString();
    OpTest<string>(s1, s2);
}

이러한 방식으로 동작하는 이유는 컴파일 시 컴파일러에는 T가 참조 형식이라는 정보만 제공되므로 모든 참조 형식에 유효한 기본 연산자가 사용되기 때문입니다. 값이 동일한지 테스트하려면 where T : IComparable<T>제약 조건을 함께 적용하고 제네릭 클래스를 생성하는 데 사용되는 모든 클래스에서 해당 인터페이스를 구현하는 것이 좋습니다.

 바인딩되지 않은 형식 매개 변수
공용 클래스 SampleClass<T>{}의 T와 같이 제약 조건이 없는 형식 매개 변수를 바인딩되지 않은 형식 매개 변수라고 합니다. 바인딩되지 않은 형식 매개 변수에는 다음과 같은 규칙이 적용됩니다.

!= 및 == 연산자를 사용할 수 없습니다. 구체적인 형식 인수에서 이러한 연산자를 지원하리라는 보장이 없기 때문입니다.

바인딩되지 않은 형식 매개 변수와 System.Object 사이에 변환하거나 이 매개 변수를 임의의 인터페이스 형식으로 명시적으로 변환할 수 있습니다.

바인딩되지 않은 형식 매개 변수를 null과 비교할 수 있습니다. 바인딩되지 않은 매개 변수를 null과 비교하는 경우 형식 인수가 값 형식이면 비교 결과로 항상 false가 반환됩니다.

 naked 형식 제약 조건
제네릭 형식 매개 변수를 제약 조건으로 사용하는 경우 이를 naked 형식 제약 조건이라고 합니다. naked 형식 제약 조건은 다음 예제에서와 같이 자체 형식 매개 변수가 있는 멤버 함수에서 해당 매개 변수를 포함 형식의 형식 매개 변수로 제한해야 하는 경우에 유용합니다.

C# 코드 복사
class List<T>
{
    void Add<U>(List<U> items) where U : T {/*...*/}
}

위 예제에서 T는 Add 메서드의 컨텍스트에서는 naked 형식 제약 조건이고 List 클래스의 컨텍스트에서는 바인딩되지 않은 형식 매개 변수입니다.

naked 형식 제약 조건은 제네릭 클래스 정의에도 사용할 수 있습니다. naked 형식 제약 조건도 다른 모든 형식 매개 변수와 마찬가지로 꺾쇠괄호 안에 선언해야 합니다.

C# 코드 복사
//naked type constraint
public class SampleClass<T, U, V> where T : V { }

컴파일러에서는 naked 형식 제약 조건이 System.Object에서 파생된다는 점을 제외하고는 이 제약 조건에 대해 어떠한 정보도 알 수 없으므로 naked 형식 제약 조건과 제네릭 클래스를 함께 사용할 필요는 거의 없습니다. 제네릭 클래스에 대해 naked 형식 제약 조건을 사용하는 경우로는 두 형식 매개 변수 사이에 반드시 상속 관계가 있도록 정의하는 경우를 들 수 있습니다.


Posted by penguindori

C# 코드 복사
public class Employee
{
    private string name;
    private int id;

    public Employee(string s, int i)
    {
        name = s;
        id = i;
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int ID
    {
        get { return id; }
        set { id = value; }
    }
}

public class GenericList<T> where T : Employee
{
    private class Node
    {
        private Node next;
        private T data;

        public Node(T t)
        {
            next = null;
            data = t;
        }

        public Node Next
        {
            get { return next; }
            set { next = value; }
        }

        public T Data
        {
            get { return data; }
            set { data = value; }
        }
    }

    private Node head;

    public GenericList() //constructor
    {
        head = null;
    }

    public void AddHead(T t)
    {
        Node n = new Node(t);
        n.Next = head;
        head = n;
    }

    public IEnumerator<T> GetEnumerator()
    {
        Node current = head;

        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }

    public T FindFirstOccurrence(string s)
    {
        Node current = head;
        T t = null;

        while (current != null)
        {
            //The constraint enables access to the Name property.
            if (current.Data.Name == s)
            {
                t = current.Data;
                break;
            }
            else
            {
                current = current.Next;
            }
        }
        return t;
    }
}

제약 조건을 사용하면 형식 T의 모든 항목이 Employee 개체이거나 Employee에서 상속된 개체임을 보장할 수 있으므로 제네릭 클래스에서 Employee.Name 속성을 사용할 수 있습니다.

여러 제약 조건을 동일한 형식 매개 변수에 적용할 수 있고 제약 조건 자체가 제네릭 형식일 수도 있습니다. 예를 들면 다음과 같습니다.

C# 코드 복사
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
    // ...
}

 

Posted by penguindori
2009. 3. 3. 17:05

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

http://msdn.microsoft.com/ko-kr/library/system.asynccallback.aspx
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Specialized;
using System.Collections;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class UseDelegateForAsyncCallback
    {
        static int requestCounter;
        static ArrayList hostData = new ArrayList();
        static StringCollection hostNames = new StringCollection();
        static void UpdateUserInterface()
        {
            // Print a message to indicate that the application
            // is still working on the remaining requests.
            Console.WriteLine("{0} requests remaining.", requestCounter);
        }
        public static void Main()
        {
            // Create the delegate that will process the results of the
            // asynchronous request.
            AsyncCallback callBack = new AsyncCallback(ProcessDnsInformation);
            string host;
            do
            {
                Console.Write(" Enter the name of a host computer or <enter> to finish: ");
                host = Console.ReadLine();
                if (host.Length > 0)
                {
                    Interlocked.Increment(ref requestCounter);
                    Dns.BeginGetHostEntry(host, callBack, host);
                 }
            } while (host.Length > 0);
            // The user has entered all of the host names for lookup.
            // Now wait until the threads complete.
            while (requestCounter > 0)
            {
                UpdateUserInterface();
            }
            // Display the results.
            for (int i = 0; i< hostNames.Count; i++)
            {
                object data = hostData [i];
                string message = data as string;
                // A SocketException was thrown.
                if (message != null)
                {
                    Console.WriteLine("Request for {0} returned message: {1}",
                        hostNames[i], message);
                    continue;
                }
                // Get the results.
                IPHostEntry h = (IPHostEntry) data;
                string[] aliases = h.Aliases;
                IPAddress[] addresses = h.AddressList;
                if (aliases.Length > 0)
                {
                    Console.WriteLine("Aliases for {0}", hostNames[i]);
                    for (int j = 0; j < aliases.Length; j++)
                    {
                        Console.WriteLine("{0}", aliases[j]);
                    }
                }
                if (addresses.Length > 0)
                {
                    Console.WriteLine("Addresses for {0}", hostNames[i]);
                    for (int k = 0; k < addresses.Length; k++)
                    {
                        Console.WriteLine("{0}",addresses[k].ToString());
                    }
                }
            }
       }
        static void ProcessDnsInformation(IAsyncResult result)
        {
            string hostName = (string) result.AsyncState;
            hostNames.Add(hostName);
            try
            {
                IPHostEntry host = Dns.EndGetHostEntry(result);
                hostData.Add(host);
            }
            catch (SocketException e)
            {
                hostData.Add(e.Message);
            }
            finally
            {
                Interlocked.Decrement(ref requestCounter);
            }
        }
    }
}
Posted by penguindori
http://msdn.microsoft.com/ko-kr/library/system.iasyncresult.aspx

using
System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncDemo
    {
        // The method to be executed asynchronously.
        public string TestMethod(int callDuration, out int threadId)
        {
            Console.WriteLine("Test method begins.");
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("My call time was {0}.", callDuration.ToString());
        }
    }
    // The delegate must have the same signature as the method
    // it will call asynchronously.
    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
}

...

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain
    {
        static void Main()
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000,
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Wait for the WaitHandle to become signaled.
            result.AsyncWaitHandle.WaitOne();

            // Perform additional processing here.
            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            // Close the wait handle.
            result.AsyncWaitHandle.Close();

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
*/


Posted by penguindori

.NET 에서Threading 라는 단어를 떠올리면 좀 막막하다거나 혹은 어디다 써먹지 하는 생각이 떠오릅니다. 그렇지만 이것 역시 개발시 거르지 않고 항상 사용하게 되는 핵심 기반 기술입니다.

Multi-Threading 이라고 하면 예전에는 서버 시스템을 구축할 때나 힘겹게 사용하던 개발 기술입니다만 요즘은 개인PC에도 하이퍼스레딩이나 다중 프로세서가 일반화된 상황이다보니 MultiThread는 뛰어난 성능의 프로그램을 만들고자하는 개발자에게는 필수 지식이 되었습니다.

Thread가 뭔가 하는 물음에 대해서는 아래 설명 및 예제들을 통해서 자연스럽게 받아들일 수 있다면 좋겠습니다.

 Thread 생성

    - 별도의 Thread로 실행시킬 Method 생성( DoSomething )

    - ThreadStart delegate(대리자) 정의

       : Thread의 Start 메소드 호출 시 실행될 메소드(DoSomething)를 등록합니다.

    - Thread 개체 생성

       : 앞서 생성한 ThreadStart delegate 개체를 생성자 파라메터로 줍니다.

    - 앞단계에서 생성한 Thread개체의 Start 메소드 호출

       : DoSomething 메소드는 이제 Main이 실행중인 Thread와 다른 새로운 Thread에서 실행되게

         됩니다.

아래 코드예를 참고하세요.

 

Multiple Threads

이번에는 DoSomething 메소드를 여러개의 다른 Thread로 실행하도록 해보겠습니다. 앞의 예제보다는 이것이 더 현실적일 거 같습니다. MultiThread로 실행시킬 일이 없다면 굳이 별도의 Thread로 실행시킬 이유도 없겠죠.

아래는 결과 화면입니다. DoSomething이 10개의 별도 Thread로 실행되었습니다.

위 결과만을 보면 너무 빨리 끝나 버리기도 하고 해서 기능적으로만 보면 단순히 DoSomething을 10번 호출하는 거랑 크게 다르지 않아 보입니다. 그래서 이번에는 효과를 시각적으로 확인할 수 있도록 DoSomething 메소드를 아래와 같이 코드를 수정했습니다.

현재 자신이 실행중인 Thread의 ManagedThreadID를 한번만 출력하던 것을 10번 출력하게 수정했습니다.  매 출력시마다 1/100초동안 멈추었다 다시 실행하도록(Sleep(10)) 했습니다. 생성된 10개의 Thread가 동시에 실행중이라고 한다면 ThreadID 값이 뒤죽박죽 섞여서 출력이 될 것입니다. 그렇지 않다면 각 ThreadID가 동일한 값이 10번씩 출력되겠죠.

아래는 결과 화면입니다.

 

Join

위 코드에 아래와 같이 Console.WriteLine ... 을 추가했습니다. "실행완료?" 라는 문자열이 어느시점에서 뿌려질까요. 아래는 코드 및 결과입니다.

Multi-Threading의 당연한 결과지만 앞서 실행시킨 10개의 Thread가 완료되기 전에 Console.WriteLine..이 실행됐습니다. 그렇지만 현재 Thread가 실행을 완료할 때까지 기다려야 하는 상황이 자주 있습니다. 이럴 경우 사용하는 것이 Thread의 Join 메소드입니다. 아래 코드 예제와 결과를 보세요.

생성된 Thread을 별도로 저장했다가 Console.WriteLine을 호출하기 전에 Join 했습니다. 이는 별도로 실행되던 각 Thread들을 Main의 Thread에 연결함으로써 내가 종료될 때까지 기다려 달라고 말하는 것입니다.

 

ThreadPriority - Thread 실행 우선 순위

Thread들의 실행은 CPU 기능이나 갯수에 따라 다르겠지만 일정한 순서없이 이루어 집니다. 그러나 특정 Thread의 실행순서를 제어해야 하는 상황이 있을 수 있습니다. 이럴 때 사용하는 것이 ThreadPriority 입니다. ThreadPriority 값은 아래와 같습니다.

===================================================================

Highest, AboveNormal, Normal(기본값), BelowNormal, Lowest

===================================================================

우선순위는 신중하게 다루는 것이 좋습니다. 사실 우선순위라는 것 자체가 Multi-Thread의 기본 정신(CPU 자원을 최대한 활용할 수 있도록 동시에 여러 작업을 수행시킨다는)에 위배되는 내용이죠. 특히 주의해야될 상황이 있습니다. 특정 스레드에 우선 순위를 너무 높게 설정할 경우 다른 스레드를 굶겨(CPU시간상)죽이는 경우가 있을 수 있고 물론 그 반대의 상황이 발생할 수도 있습니다.

 

Thread의 중단 & ThreadAbortException

 

 

Execution Context

[출처] Threading - (1) 기초|작성자 유후


Posted by penguindori

동시에 실행되는 여러개의 Thread가 동시에 같은 변수(예에서 Count)를 수정할 경우 문제가 발생할 수 있습니다.

아래 그림을 보세요.

 

두 Thread가 연산을 했으니까 당연히 Count는 12가 되어야 하지만 결과는 11입니다.

이와 같은 Thread간의 데이터 공유 문제를 해결하기 위해서 .NET Framework은 상황에 맞는 다양한 해결책을 제시하고 있습니다.

 

Interlocked Class

증가(1증가), 감소(1 감소)와 같은 증감 연산시에 사용합니다. 위의 코드를 Interlocked 클래스를 사용하도록 변경하면 아래와 같습니다.

    static void UpdateCount()

    {

        for(int i=0;i<10000;i++)

        {

            Interlocked.Increment(ref count);

        }

    }

 

Interlocked.Increment(count); 이 코드가 특정 Thread에 의해 실행되는 동안 다른 Thread는  count 변수에 접근할 수 없습니다.

아래는 Interlocked Class가 제공하는 static method 목록입니다.

 Add  두 값을 더한다.
 Decrement  1을 뺀다.
 Exchange  두 값을 바꾼다.
 Increment  1을 더한다.
 Read  64비트 수를 읽는다.

 

Synchronization Locks

Interlocked 클래스를 사용하면 특정 값에 대한 단위실행(atomic operation)을 보장할 수 있습니다만 더 큰 단위인 object에 대한 처리라면 어떻게 될까요. 보통 object는 여러개의 값을 멤버로 가지게 되고 이에 대해 연산이 수행됩니다. 물론 이것도 object내에서 특정 변수 연산에 Interlocked 클래스를 사용하면 되지 않겠냐고 생각할 수도 있겠지만 단순하지 않습니다.

아래 예제를 보세요.

Counter 클래스를 별도로 만들었습니다. 전체 갯수(count) 및 count가 짝수일 경우 1 증가되는 evenCount가 있습니다. UpdateCount 메소드 내에서 이들 값에 대한 조작시 Interlocked를 사용했기때문에 이들 값이 CPU의 레지스터에 올라가 있는 동안 다른  Thread가 접근 못할 것이고 결과는 우리가 원하는 count=10만, evenCount=5만이 될 것입니다....라고 상상해볼 수 있습니다만 살펴보면 문제가 있습니다.

Interlocked.Increment(ref count)는 문제없습니다. 실행결과는 항상 10만으로 정확합니다. 그러나 evenCount가 계산되는 과정을 보세요.

어느정도 진행되어 count 값이 현재 9(홀수) 라고 가정합니다.

1. Thread1 이 Interlocked.Increment(ref count)를 실행하여 count=10 이 된다.

2. 1이 완료되고 Thread1이 Interlocked.Increment(ref evenCount)을 실행하기전에 Thread2 가 Interlocked.Increment(ref count)을 실행되어 count=11 이 된다.

3. Thread1이 Interlocked.Increment(ref evenCount)을 실행하려고 보니가 count값이 11이어서 evenCount에 대한 1증가 연산이 이루어지지 않는다.

이 상황이라면 10일때 evenCount가 1증가 이뤄져야하는 상황이 사라져 버리는 결과가 오게됩니다. 이와 같은 문제를 해결하기 위해 Synchronization lock이라는 기능을 제공합니다.

Synchronization lock 을 이용하면 UpdateCount를 아래와 같이 수정할 수 있습니다.

위처럼 특정 코드 영역을 lock 키워드를 사용해서 묶습니다. 이는 이 코드 영역이 특정 Thread에 의해 실행되는 동안 다른 Thread는 이 코드 영역을 실행할 수 없도록 합니다.

 

Monitor

Synchronization lock의 이면에는 실제로 Monitor 클래스가 작동을 합니다.  아래는 Monitor 클래스의 주요 static 메쏘드입니다.

 Enter  명시한 object에 lock을 건다.
 Exit  명시한 object에 걸린 lock을 해제한다.
 TryEnter  Enter와 같지만 lock을 얻기까지의 최대 시간(timeout)을 지정한다.
 Wait  lock을 해제하지만 다시 그 lock을 얻을때까지 현재 Thread를 중단한다.
   

Monitor 클래스를 이용해서 앞의 Counter 클래스를 변경하면 아래와 같습니다.

 

Deadlock

데드락은 두 쓰레드가 동일 자료에 대해서 lock을 동시에 요청함으로서 lock을 얻지 못하고 무한 대기상태에 빠지는 것을 말합니다.

위 코드에서 데드락이 발생하는 절차는 아래와 같습니다.

1. threadA가 먼저 실행되어 MethodA 코드에 따라 data1에 lock을 건다.

2. threadB가 실행되어 MethodB 코드에 따라 data2에 lock을 건다.

3. threadA가 threadB가 잡고 있는 data2에 대한 lock이 풀릴때까지 대기한다.

4. threadB는 threadA가 잡고 있는 data1에 대한 lock이 풀릴때까지 대기한다.

5. 무한대기 상태로 돌입한다.

 

이러한 데드락에 대한 해결책은 따로 없습니다. 개발자가 신중하게 개발해야 한다는 것 외에는..

조언이 될만한 것이라면 Monitor.TryEnter를 사용해서 타임아웃을 지정하거나 하는 정도일 것입니다. 가능하면 lock 거는 범위를 최소화 하는 것도 좋을 것입니다.

 

다음에는 Mutex, Semaphore, AutoResetEvent, ManualResetEvent 에 대해서 추가로 알아볼 생각입니다.

Posted by penguindori

Multi-Thread(이하 MT) 응용프로그램을 개발한다는 것은 컴퓨터 자원을 최대한 활용하여 성능을 높인다는 면에서 좋은 일이지만 개발자가 절대 간과해서 안되는 것이 하나 있습니다. 바로 MT간의 데이터 공유 문제입니다. 간단히 생각해 봐도 문제가 생길 여지가 많음을 쉽게 떠올릴 수 있습니다. 예를 들어 10개 정도의 Thread가 동시에 하나의 공유(전역으로 선언된 static 같은...) 변수를 조작한다고 할 때 그 값의 변화들이 개발자가 예상하는 대로 나와줄까요.

 아래 코드예를 보세요.

 

아래는 실행결과입니다.

 

위의 코드에서 count 값을 1 증가시키는 연산명령이 컴퓨터상에서 물리적으로 처리되는 과정을 나열해 보면 아래와 같습니다.

    1. 메모리에 있는 count의 현재값을 프로세서의 레지스터(register)로 읽어들인다.

    2. 레지스터에 읽어들인 그 값을 1만큼 증가시킨다.

    3. 레지스터에서 변경된 그 값을 메모리로 복사한다.

위 처리 절차 자체로는 아무 문제가 없습니다. 원래 CPU 역할이 그거니까. 문제는 프로세서가 여러개인 경우 또는 Hyper Threading(하나지만 CPU가 여러개인 것처럼 연산을 동시에 할 수 있는)을 지원하는 CPU인 경우에는 MT하에서는 아래와 같은 문제가 발생할 수 있습니다.

Posted by penguindori

ReaderWriterLock

읽을 때는 여러 쓰레드가 동시에 읽을 수 있고 쓰기를 할 때만 배타적 잠금을 할 수 있도록 해주는 것이 ReaderWriterLock 클래스입니다.

ReaderLock이 걸린 상황에서는 다른 쓰레드도 같은 자원에 접근해서 읽을 수 있습니다. WriterLock이 걸리면 다른 쓰레드는 접근할 수 없습니다. 단 WriterLock을 얻기 위해서는 현재 걸려있는 모든 ReaderLock이 해제되어야 합니다.

ReaderWriterLock 클래스 Method 입니다.

 IsReaderLockHeld  ReaderLock 걸려있는지 여부
 IsWriterLockHeld  WriterLock 걸려있는지 여부
 AcquireReaderLock  지정 시간내에 ReadLock을 건다. 시간초과시 응용프로그램 오류.
 AcquireWriterLock  지정 시간내에 WriterLock을 건다. 시간초과시 응용프로그램 오류.
 DowngradFromWriterLock  WriterLock을 ReaderLock으로 변경
 ReleaseReaderLock  ReaderLock 해제
 ReleaseWriterLock  WriterLock 해제
 UpgradeToWriterLock  ReaderLock을 WriterLock으로 변경

 

[예제]

참고로 Downgrade/Upgrade시에는 이전 Lock 수준으로 복귀할 때 필요한 정보로 LockCookie를 사용합니다.

 

지금까지의 synchronization lock은 thread간의 데이터공유에 한정하여 사용된 것들입니다. 운영체제의 커널(kernel)에서 제공되는 object인 Mutex, Semaphore, Event(AutoResetEvent, MaunalResetEvent) 등은 AppDomain 및 Process 경계를 넘어 데이터를 공유하고 lock을 걸 수 있는  기능을 제공합니다. 이들의 공통점은 WaitHandle을 Base 클래스로 하고 있다는 것입니다. WaitHandle은 Win32 동기화 핸들을 캡슐화한 클래스로 현재 특정 공유자원에 대해 배타적 접근(lock)을 하고 있는 쓰레드간에 신호를 주고 받을 수 있도록 하여 공유자원에 대한 배타적 접근(lock)을 원하는 오브젝트들이 제대로 작동할 수 있도록 해주는 메카니즘을 제공합니다.

 

Mutex

 

Semaphore

 

Event

AutoResetEvent

ManualResetEvent

 

Posted by penguindori