C# TaskFactory and TaskScheduler

http://msdn.microsoft.com/ko-kr/library/dd321418.aspx

http://code.msdn.microsoft.com/ParExtSamples

http://msdn.microsoft.com/ko-kr/library/dd997402.aspx

http://channel9.msdn.com/Events/TechDays/Techdays-2012-the-Netherlands/2287


 이전 포스트에서 "new TaskFactory(new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler);"와 같은 형식으로 TaskScheduler를 배타적 실행이 되도록 하여 Task.Yield()를 테스트를 하였다. ( 2013/03/11 - [.Net Framework] - [C# Snippet]Task-based Asynchronous Pattern Task.Yield ) 이번에는 이 작업 스케줄러에 대해서 커스터마이징을 하는 방법에 대해서 알아 보고 Task 수행을 마음대로 요리할 수 있는 방법에 대해서 알아 보고자 한다.


 기본 TaskScheduler은 CPU Core의 갯수에 맞춰서 동시 실행이 되도록 되어 있습니다. 이 스케줄러는 일반 적인 케이스(I/O, CPU, Network, ...)에서 노말하게 동시 실행이 되도록 배분을 하고 있다. 그렇지만 Network 대기 시간같은 응답 대기에서는 경합에 의한 CPU 손실보다는  보다 많은 실행을 동시에 수행하는 것이 더 중요할 때가 있다.  보다 많은 동시 실행을 요구하는 시나리오에서 기본 스케줄러가 이닌 커스터마이징 된 스케줄러를 통해서 동시 실행 갯수를 통제 할 수 있을 것이다. ( ThreadPool.QueueUserWorkItem(new WaitCallback(_ => { })); 와 같은 비슷한 패턴으로 TaskFactory로 생성된 타스크가 수행된다. 하지만 경합 관리에서는 다른 패턴으로 관리가 되고 있다. http://www.codethinked.com/net-40-and-systemthreadingtasks)


 참고로 ThreadPool과 TaskPool의 스케줄링에 의한 실행 결과를 "그림1"을 통해 확인해 보자

[그림1] Thread and Task 실행 비교

http://www.codethinked.com/net-40-and-systemthreadingtasks

( Thread는 ThreadPool.QueueUserWorkItem을 통해 실행 됨 )


 이와 같이 스케줄링에 의해서 총 실행 결과에 영향을 미칠 수가 있다. 이제 본격적으로 TaskScheduler을 좀더 파헤처 보도록 하자. "코드1"에서는 기본적으로 제공하는 스케줄러를 가져오는 방법이다.

TaskScheduler scheduler = TaskScheduler.Default;

[코드1] 기본 제공되는 Scheduler


 이 스케줄러는 Task.Factory와 같은 형식으로 스케줄리이 되는 기본 스케줄러다. 이제는 MyTaskScheduler를 만들어 수행 순서를 마음대로 조절하는 Scheduler를 만들어 볼 것이다.

/// <summary>
/// TaskScheduler을 상속 받아 구현 한다.
/// </summary>
public class MyTaskScheduler : TaskScheduler
{
    protected override System.Collections.Generic.IEnumerable<Task> GetScheduledTasks()
    {
        throw new NotImplementedException();
    }
 
    protected override void QueueTask(Task task)
    {
        throw new NotImplementedException();
    }
 
    /// <remarks>재진입으로 인한 오류를 방지하기 위해 작업 인라이닝은 관련된 스레드의 로컬 큐에서 대기 중인 대상이 있는 경우에만 발생합니다.</remarks>
    protected override bool TryExecuteTaskInline(Task taskbool taskWasPreviouslyQueued)
    {
        throw new NotImplementedException();
    }
}

[코드2] TaskScheduler abstract class를 상속 코드


 "코드2"는 추상 TaskScheduler을 상속받은 모습이다. 이제 해당 메소드를 하나씩 채워 나가면 나만의 Scheduler이 만들어 지는 것이다. 생각보다 간단해 보이지 않는가? 이제 간단하게 완성된 MyTaskScheduler를 살펴 보자.

public class MyTaskScheduler : TaskScheduler
{
    /// <summary>연결된 쓰레드에서 동기 Task 제공해 준다</summary>
    /// <param name="task">큐에 대기할 Task입니다.</param>
    protected override void QueueTask(System.Threading.Tasks.Task task)
    {
        TryExecuteTask(task);
    }
 
    /// <summary>연결된 쓰레드에서 동기 Task 제공해 준다</summary>
    /// <param name="task">실행할 타스크</param>
    /// <param name="taskWasPreviouslyQueued">작업이 이전에 큐에 대기되었는지 여부를 나타내는 부울입니다.이 매개 변수가 True이면 작업이 이전에 큐에 대기된 것일 수 있습니다. False이면 작업이 큐에 대기되지 않은 것입니다. 작업을 큐에 대기하지 않고 인라인으로 실행하려면 이 호출을 수행합니다.</param>
    /// <returns>작업이 인라인으로 실행되었는지 여부를 나타내는 부울 값입니다. 성공적인 실행 시 True, 그 이외에 false</returns>
    /// <remarks>재진입으로 인한 오류를 방지하기 위해 작업 인라이닝은 관련된 스레드의 로컬 큐에서 대기 중인 대상이 있는 경우에만 발생합니다.</remarks>
    protected override bool TryExecuteTaskInline(System.Threading.Tasks.Task taskbool taskWasPreviouslyQueued)
    {
        return TryExecuteTask(task);
    }
 
    /// <summary>디버거를 지원하기 위해 현재 스케줄러의 큐에 대기되어 실행을 기다리고 있는 Task 인스턴스의 열거 가능한 형식을 생성합니다.</summary>
    /// <returns>디버거가 현재 이 스케줄러의 큐에 대기된 작업을 트래버스할 수 있도록 허용하는 열거 가능한 형식입니다.</returns>
    protected override IEnumerable<System.Threading.Tasks.Task> GetScheduledTasks()
    {
        return Enumerable.Empty<System.Threading.Tasks.Task>();
    }
 
    /// <summary>이 TaskScheduler가 지원할 수 있는 최대 동시성 수준을 나타냅니다.</summary>
    public override int MaximumConcurrencyLevel { get { return 1; } }
}

[코드3] MyTaskScheduler


 MyTaskScheduler는 QueueTask로 들어온 Task를 TryExecuteTask를 통해서 바로 실행이 되도록 하였기에 TryExecutTaskInline로 호출이 되지 않도록 되어 있다. 만약 여기에서 실행이 되지 않으면 TryExecuteTaskInline로 다시 시도하게 된다. 지금 구현된 MyTaskScheduler은 기본적으로 동시 실행이 1개만 되도록 설계 및 구현 되었으며 다음에 좀더 확장하여 살펴 보도록 하겠다. 참고로 TaskSchedler.Default의 MaximumConcurrencyLevel은 2147483647이다. 이제 "코드4"를 통해서 실행하는 코드를 살펴 보자

/// <summary>
/// 커스터 마이징된 MyScheduler로 테스트
/// </summary>
public void MyScheduler_TestMethod()
{
    // 시간을 재기 위해서 사용
    Stopwatch sw = new Stopwatch();
    sw.Start();
 
    TaskScheduler myScheduler = new MyTaskScheduler();
    // 기본 스케줄러
    // myScheduler = TaskScheduler.Default;
    // 동시 실행 갯수 확인
    // Console.WriteLine(myScheduler.MaximumConcurrencyLevel);
 
    // 커스터마이징 된 MyScheduler을 이용해 TaskFactory를 생성 하도록 한다.
    var factory = new TaskFactory(myScheduler);
    var tasks = new List<System.Threading.Tasks.Task>();
 
    // 동시 실행이 되도록 수행
    for (int j = 1j <= 200j++)
    {
        var task = factory.StartNew(() =>
        {
            for (int i = 0i < 500i++)
            {
                var a = Thread.CurrentThread.ManagedThreadId;
                Console.WriteLine("{0} on thread {1}"iThread.CurrentThread.ManagedThreadId);
            }
        });
 
        tasks.Add(task);
    }
 
    // 모두 완료가 될 때까지 대기
    System.Threading.Tasks.Task.WaitAll(tasks.ToArray());
 
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);
}

[코드4] MyScheduler 테스트 코드


 위 코드를 통해 결과 화면을 "그림1"에서 확인해 보자.


[그림1] MyTaskScheduler 방식으로 실행 결과 화면


 위 결과를 보면 여러 타스크가 실행이 되었지만 하나의 쓰레드 ID에서 실행이 된 것을 확인 할 수 있다. 지금까지 간단하가 MyScheduler은 TaskScheduler에서 제공하는 기본만을 가지고 기본 개념을 이해하기 위해 연습을 한 것이며 다음 포스트에서 좀더 디테일한 작업을 진행해 보도록 하겠다.



소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략하도록 하겠습니다. 포스트의 내용이 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 하기 위해 노력하였습니다.. 실제 개발에서도 필요한 소스는 단순히 Copy & Paste 만으로도 사용할 수 있습니다. 그리고 주석을 이용해 nDoc이나 별도의 자동 Document 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다. 
※ DOC에 대한 프로그램 정보 Util link

ing™       


델리게이트를 이용한 (비 동기)실행

http://msdn.microsoft.com/ko-kr/library/2e08f6yc.aspx

http://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.110).aspx

http://msdn.microsoft.com/ko-kr/library/system.delegate.aspx



 처음 델리게이트(delegate)를 알게 되었을 때 필자는 이걸 어디에 사용하지?, Super class(interface, abstract class)를 사용해서 한번에 실행 하는것과는 어떤 차이가 있는거지? 한번에 모아서 실행하지 않고 그냥 그때 그때 실행해도 상관 없지 않을까? 잘 모르니 나중에 나중에 나중에~~~ 

이렇게 미루다가 오랜 시간 후 event 선언시에 델리게이트를 이용한다고만 알고 있었습니다. 그렇지만 이번 포스트에서는 아주 유용하게 비 동기로 실행 할 수 있도록 도와주는 델리게이트를 알아 보고자 한다.


2013/02/12 - [.Net Framework] - [Delegate] 똑똑하게 써보자 델리게이트! - 참고 사항


 동기 메소드를 비 동기 적으로 호출하여 실행 할 수 있도록 델리게이트를 사용해 구현 할 수 있다. 구현 방법은 비 동기로 실행할 메소드의 Argument와 동일한 파라메터를 가지는 델리게이트를 선언하고 BeginInvoke를 통해서 비 동기적으로 실행 하도록 한다. 아래 코드를 보면 쉽게 이해할 수 있을 것이다.

//델리게이트 선언
public delegate string AasyncDelegateCaller(int callDurationout int threadId);

[코드1] 델리게이트 선언


public string AasyncDelegate(int callDurationout int threadId)

[코드2] 비 동기로 실행을 원하는 함수


 "코드1"에서 델리게이트를 선언하고 "코드2"에서 비 동길로 실행을 원하는 메소드이다. 그렇다면 이제 이 둘을 연결하여 사용하는 부분을 보도록 하자.

// 델리게이트 인스턴스 생성
AasyncDelegateCaller caller = new AasyncDelegateCaller(AasyncDelegate);
 
// 비 동기 시작
IAsyncResult result = caller.BeginInvoke(3000out threadIdnullnull);

[코드3] 델리게이트를 통해서 비 동기 실행


 선언된 델리게이트를 인스턴스화 하고 파라메터를 비 동기 실행할 메소드를 입력해 줌으로써 사용할 준비가 된 것이다. 그렇다면 이제 BeginInvoke를 통해서 실행하면 메인 쓰레드와는 별개의 다른 쓰레드에서 실행되는 것을 확인 할 수 있을 것이다. 참고로 Invoke로 실행하면 기존과 같이 메인 쓰레드에서 동기적으로 실행이 될 것이다. 이제 전체 코드를 통해서 직접 확인을 해 보기를 바란다.

//델리게이트 선언 public delegate string AasyncDelegateCaller(int callDurationout int threadId); static void Main(string[] args) {     Program p = new Program();     // 비 동기 실행에서 쓰레드 번호를 알아 내는데 사용 함.     int threadId; // 델리게이트 인스턴스 생성 AasyncDelegateCaller caller = new AasyncDelegateCaller(p.AasyncDelegate); // 비 동기 시작 IAsyncResult result = caller.BeginInvoke(3000out threadIdnullnull);     //비 동기 메소드에 제어권이 넘어 가도록 설정 함. //Thread.Sleep(0); //일반적인 상황에서는 필요 없음. 제어권이 넘어가지 않으면 실행

    Console.WriteLine("메인 쓰레드 {0} 에서 작업."Thread.CurrentThread.ManagedThreadId);     // EndInvoke를 통해서 비 동기 실행이 완료 될 때까지 대기 함.     string returnValue = caller.EndInvoke(out threadIdresult);     Console.WriteLine("쓰레드 {0} 에서 작업, 반환 값 : \"{1}\"."threadIdreturnValue);     Console.WriteLine("완료");     Console.ReadKey(); } public string AasyncDelegate(int callDurationout int threadId) {     Console.WriteLine("테스트 메소드 시작");     Thread.Sleep(callDuration);     threadId = Thread.CurrentThread.ManagedThreadId;     return String.Format("실행 시간 {0} ms."callDuration.ToString()); }

[코드4] 비 동기 실행 예제 코드


BeginInvoke로 실행하면 언제나 EndInvoke를 실행해 줘야 한다. EndInvoke가 해주는 역할은 비 동기 실행이 완료가 될때까지 대기하도록 해 주며 IAsyncResult의 WaitHandler에 완료 상태가 되도록 해준다. 이제 코드를 실행해 보면 아래 "그림1"과 같은 결과를 확인할 수 있을 것이다. ( 이 예제에서는 IAsyncResult를 통해서 대기하도록 하는 코드는 제외되었고 Async 관련 포스트에서 곤련 정보를 얻을 수 있을 것이므로 생략 하도록 하겠다 )


[그림1] 비 동기 실행 결과 화면


 메인 쓰레드 ID는 1번이고 비 동기로 실행되는 쓰레드 ID는 3번을 가리키고 있다. 


지금까지 간단하게 델리게이트를 이용해서 비 동기로 실행하는 방법에 대해서 알아 보았다. 그렇지만 이 케이스는 우리가 진짜로 원하는 방식의 비 동기 실행이라고 할 수 없다. 이 코드는 결과 적으로 메인 쓰레드에서 대기 하는 코드인 EndInvoke(IAsyncResult의 AsyncWaitHandle.WaitOne())가 메인 쓰레드에서 대기토록 하게 되어 있다. 만약 UI를 가진 응용 프로그램에서 위와 같은 구조를 가진다면 비 동기로 실행하는 이점이 없는 것이다. 이제 EndInvoke를 별도의 쓰레드에서 실행 되도록 구조를 변경해 보도록 하겠다.

public void AsyncDelegate_TestMethod2()
{
    // 비 동기 실행에서 쓰레드 번호를 알아 내는데 사용 함.
    int threadId;
 
    // 델리게이트 인스턴스 생성
    AasyncDelegateCaller caller = new AasyncDelegateCaller(AasyncDelegate2);
 
    // 비 동기 시작
    IAsyncResult result = caller.BeginInvoke(3000out threadIdnew AsyncCallback(AsyncDelegateCallback), caller);
 
    Console.WriteLine("메인 쓰레드 {0} 에서 작업."Thread.CurrentThread.ManagedThreadId);
 
    Console.WriteLine("완료");
}
 
public string AasyncDelegate2(int callDurationout int threadId)
{
    Console.WriteLine("테스트 메소드 시작");
    Thread.Sleep(callDuration);
    threadId = Thread.CurrentThread.ManagedThreadId;
    return String.Format("실행 시간 {0} ms."callDuration.ToString());
}
 
public void AsyncDelegateCallback(IAsyncResult result)
{
    AasyncDelegateCaller caller = (AasyncDelegateCaller)result.AsyncState;
 
    // 비 동기 실행에서 쓰레드 번호를 알아 내는데 사용 함.
    int threadId;
 
    // EndInvoke를 통해서 비 동기 실행이 완료될때까지 대기 함.
    string returnValue = caller.EndInvoke(out threadIdresult);
 
    Console.WriteLine("쓰레드 {0} 에서 작업, 반환 값 : \"{1}\"."threadIdreturnValue);
}

[코드5] 별도의 쓰레드에서 EndInvoke를 실행 함.

(중요 변경 사항을 "             "  배경색으로 표기 하였다)


 위 "코드5"와 같이 수정으로 메인 쓰레드에서 EndInvoke를 실행하지 않고 AsyncDelegateCallback 메소드( 별도의 쓰레드에서 호출 되어 실행 됨 )에서 실행이 되도록 하였다. 이런 구조로 변경이 되면서 얻을 수 있는 이점은 메인 쓰레드에서는 언제 비 동기 실행이 종료가 되는지 상관 없이 메인 쓰레드에서는 별도의 작업을 계속 진행하도록 할 수 있다. 관심의 분리를 통해 메소드 범위에서 집중할 수 있는 범위를 나눠줄 수가 있는 것이다. 이제 결과 화면을 확인해 보자.


[그림2] 비 동기 호출의 결과를 Callback에서 처리


 결과 화면이 이전과는 약간 다르게 표시가 되엇다. 이 이유는 메인 쓰레드에서 Waiting(WaitOne(), EndInvoke()) 하는 작업 없이 메인 쓰레드는 계속 진행하도록 되어 있고 약 3초 후에 별도의 쓰레드에서 EndInvoke를 통해 비 동기 결과값을 화면에 뿌려주는 작업이 진행되기 때문에 위와 같은 결과로 완료가 된 것이다. "그림1"을 통해 쓰레드 실행 흐름을 보도록 하자


[그림3] 비 동기 실행


 "그림3"에서처럼 메인 쓰레드에서 비 동기 쓰레드를 활성화 시키고 메인 쓰레드는 계속 진행하여 실행이 되고 있으며 활성화된 비 동기 쓰레드는 완료가 될 때가지 메인 쓰레드와는 별개로 실행이 된다. 이런 흐름에 의해서 "그림2"에 해당하는 화면이 결과로 뿌려지게 된 것이다.




 참고로 델리게이트는 모든 .Net Framework 버전에서 지원되는 기능이 아니므로 아래 "표1"을 참고하도록 하자.


Delegate를 지원하는 프레임웍 정보

.NET Framework

4.5, 4, 3.5, 3.0, 2.0, 1.1, 1.0에서 지원

.NET Framework Client Profile

4, 3.5 SP1에서 지원

이식 가능한 클래스 라이브러리

이식 가능한 클래스 라이브러리에서 지원

Windows 스토어 앱용 .NET

Windows 8에서 지원 

[표1] 델리게이트 프레임웍 지원 정보



소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략하도록 하겠습니다. 포스트의 내용이 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 하기 위해 노력하였습니다.. 실제 개발에서도 필요한 소스는 단순히 Copy & Paste 만으로도 사용할 수 있습니다. 그리고 주석을 이용해 nDoc이나 별도의 자동 Document 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다. 
※ DOC에 대한 프로그램 정보 Util link

ing™       



Task-based Asynchronous Pattern Task.Yield()

http://blog.stephencleary.com/2012/08/async-coroutines.html

http://msdn.microsoft.com/ko-kr/library/system.threading.tasks.concurrentexclusiveschedulerpair.aspx

http://blog.stephencleary.com/2012/08/async-and-scheduled-concurrency.html

http://msmvps.com/blogs/jon_skeet/archive/tags/Eduasync/default.aspx



 이번 포스트에서는 Task에서 실행되는 주기를 컨트롤 할 수 있는 객체 중에 기본 TaskFactory대신 배타적인 실행 주기로 수행하는 ConcurrentExclusiveSchedulePair 클래스를 사용하여 스케줄링 하는 방법을 알아 보자. 이 코드는 Consone 프로젝트에서 테스트를 진행 하였다.

static void Main(string[] args)
{
    Program p = new Program();
    p.TAP_Yield_TestMethod();
 
    Console.ReadKey();
}
 
#region Task test the Yield method
//배타 스케줄에서 Task 스케줄 확인
public async void TAP_Yield_TestMethod()
{
    var task = MainAsync();
    task.Wait();
}
 
/// <summary>
/// 배타적으로 Task가 실행되는 scheduler.
/// </summary>
private static TaskFactory coroutineFactory = new TaskFactory(new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler);
 
//기본 TaskFactory 선언 방법
//private static TaskFactory coroutineFactory = new TaskFactory();
 
/// <summary>
/// 기본 TaskFactory 대신 재 정의 한 TaskFactory를 통해 Task 생성하여 실행
/// </summary>
private static async Task RunCoroutineAsync(Func<Task> coroutine)
{
    //배타 실행 스케줄로 세팅된 TaskFactory
    //StartNew로 만들어진 Task를 배타 실행되는 스케줄로 관리가 되도록 컨트롤 한다.
    await await coroutineFactory.StartNew(coroutine);
}
 
/// <summary>
/// 여러 루틴을 한번에 실행 하도록 함
/// </summary>
/// <returns></returns>
static async Task MainAsync()
{
    var coroutines = new[]
{
    RunCoroutineAsync(FirstCoroutine),
    RunCoroutineAsync(SecondCoroutine),
    RunCoroutineAsync(ThirdCoroutine),
};
 
    //모두 완료가 될 때가지 대기 한다.
    await Task.WhenAll(coroutines);
}
 
//첫번째 실행
private static async Task FirstCoroutine()
{
    Console.WriteLine("Starting - Zone #1");
    Console.WriteLine("Yielding - Zone #1");
 
    //배타 실행 스케줄에서 Task가 실행 됨으로 다음 Task에 실행 수행을 넘긴다.
    await Task.Yield();
 
    Console.WriteLine("Returned - Zone #1");
    Console.WriteLine("Yielding again - Zone #1");
 
    //배타 실행 스케줄에서 Task가 실행 됨으로 다음 Task에 실행 수행을 넘긴다.
    await Task.Yield();
 
    Console.WriteLine("Returned - Zone #1");
    Console.WriteLine("Finished - Zone #1");
}
 
//두번째 실행
private static async Task SecondCoroutine()
{
    Console.WriteLine("  Starting - Zone #2");
    Console.WriteLine("  Yielding - Zone #2");
 
    //배타 실행 스케줄에서 Task가 실행 됨으로 다음 Task에 실행 수행을 넘긴다.
    await Task.Yield();
 
    Console.WriteLine("  Returned - Zone #2");
    Console.WriteLine("  Yielding again - Zone #2");
 
    //배타 실행 스케줄에서 Task가 실행 됨으로 다음 Task에 실행 수행을 넘긴다.
    await Task.Yield();
 
    Console.WriteLine("  Returned - Zone #2");
    Console.WriteLine("  Yielding again - Zone #2");
 
    //배타 실행 스케줄에서 Task가 실행 됨으로 다음 Task에 실행 수행을 넘긴다.
    await Task.Yield();
 
    Console.WriteLine("  Returned again - Zone #2");
    Console.WriteLine("  Finished - Zone #2");
}
 
//세번째 실행
private static async Task ThirdCoroutine()
{
    Console.WriteLine("    Starting - Zone #3");
    Console.WriteLine("    Yielding - Zone #3");
 
    //배타 실행 스케줄에서 Task가 실행 됨으로 다음 Task에 실행 수행을 넘긴다.
    await Task.Yield();
 
    Console.WriteLine("    Returned - Zone #3");
    Console.WriteLine("    Finished - Zone #3");
}
#endregion

[코드1] Exclusive TaskFactory


 위 코드를 Console를 통해 확인해 보자. 확인 된 결과는 "그림1"을 통해 확인할 수 있다.


[그림1] Exclusive TaskFactory 실행 화면


 실행이 Task.Yield()를 만났을 때마다 다음 Task가 실행되는 것을 확인 할 수 있다. 그러다면 아래와 같이 Default TaskFactory를 통해 배타수행과 비교를 해 보도록 하자. 아래 "코드2" 처럼 수정해 보도록 하자.


private static TaskFactory coroutineFactory = new TaskFactory();

[코드2] Default TaskFactory를 활성화 하는 코드



 "코드1"에서 "코드2" 부분을 주석 해제 하여 활성화 하여 테스트를 해 보았다. "그림2"를 통해 확인할 수 있다. 


[그림2] Default TaskFactory를 통해 실행된 화면



 위 소스를 보고 직접 실행 스케줄에 대해서 테스트를 해 보면서 수행 순서에 대해서 이해를 해 보도록 하자. 이와 같이 별도의 스케줄러를 생성하여 원하는 대로 Task를 수행할 수 있도록 커스터마이징도 할 수 있다.


 이 포스트에 있는 내용이 언제나 확실한 정답은 아닙니다. 진실이라고 생각해 왔던 전제가 시간의 지남에 따라 들어나지 않았던 다른 이면 때문에 좋은 방향으로 이끌어 낼 수 있는 역할로 변환 되는게 역사적으로도 많은 증명 있었습니다. 그렇지만 저는 현재 상황에서 최선의 답을 찾고자 노력하였으며 이 글을 읽는 다른 분들에게 다음 길을 갈 수 있도록 도와주는 디딤돌이 되고자 노력하고자 포스팅을 통해 공유하고자 하는 것입니다. 그리고 프로그래머라는 타이틀을 달고 살아야 한다면 "왜"라는 의문을 항상 가지고 다니면서 자신의 위치에 안주하지 않고 항상 노력하는 모습으로 살아 가고자 합니다. 언제든 지적이나 오류가 있으면 피드백 부탁 드리겠습니다.

ing™       


Task.ContinueWith



 Task 기반 비 동기 작업을 수행하다가 보면 순차적으로 수행해야 하는 작업이 있을 수 있다. 그럴때마다 await 키워드로 다시 비동기를 실행하지 않고 ContinueWith를 사용하면 순차적인 비 동기를 수행 할 수 있다. 아래 구문을 살펴 보자.


public async void TAP_AsyncAwait_TestMethod2()
{
    //.Net framework 4.5에 추가된 메소드로 
    //Task.Factory.StartNew 래핑한다.
    var returnValue = await Task.Run(() => { /* Something */ return 10; })
        //10을 리턴하면 아래 구문을 수행 한다.
        .ContinueWith(_result => { return _result.Result + 100; })
        //110을 리턴하면 아래 구문을 수행 한다.
        .ContinueWith(_return => { Thread.Sleep(1000); return _return.Result + 200; })
        //1초 후 310을 리턴하면 아래 구문을 수행 한다.
        //결과 값을 String으로 반환 한다.
        .ContinueWith(_return => { return _return.Result.ToString(); });
 
    Console.WriteLine(returnValue);       
}

[코드1] ContinueWith로 비 동기 순차 실행


 이와 같이 사용하여 순차적인 비 동기 실행을 구현하여 await를 남발하지 않도록 하면 될 것이다.



소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략하도록 하겠습니다. 포스트의 내용이 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 하기 위해 노력하였습니다.. 실제 개발에서도 필요한 소스는 단순히 Copy & Paste 만으로도 사용할 수 있습니다. 그리고 주석을 이용해 nDoc이나 별도의 자동 Document 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다. 
※ DOC에 대한 프로그램 정보 Util link

ing™       





Task.Run() 메소드


 .Net framework 4.5로 업그레이드 되면서 기존에 사용하고 있던 Task.Factory.StartNew() 메소드가 다음의 구문으로 쉽게 사용할 수 있도록 추가 되었다. 

//Factory를 통해 비 동기 Task를 반환
Task.Factory.StartNew(() => { /* Something */ });
 
//.Net framework 4.5에 추가된 메소드로 
//Task.Factory.StartNew 래핑한다.
Task.Run(() => { /* Something */ });

[코드1] 비 동기 실행 Task 수행 코드


 물론 위 코드도 Generic을 지원하고 있으며 .Net framework 4.5에서는 간결하게 래핑한 Run() 메소드를 사용하는걸 권장한다. 다만 일반 사용 보다는 정교하게 비 동기 작업에 대해서 컨트롤을 해야 할 때는 기존과 같은 방법으로 Factory의 StartNew를 사용해서 사용해야 할 것이다.



소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략하도록 하겠습니다. 포스트의 내용이 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 하기 위해 노력하였습니다.. 실제 개발에서도 필요한 소스는 단순히 Copy & Paste 만으로도 사용할 수 있습니다. 그리고 주석을 이용해 nDoc이나 별도의 자동 Document 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다. 
※ DOC에 대한 프로그램 정보 Util link

ing™       




C# Task-based Asynchronous Pattern - TAP(2)

async / await keyword

http://msdn.microsoft.com/ko-kr/library/hh873175.aspx

http://msdn.microsoft.com/en-us/library/hh873175.aspx


http://www.albahari.com/threading/part2.aspx#_Thread_Safety


2013/02/26 - [.Net Framework] - [Async]Task-based Asynchronous Pattern - TAP(1) Async #7


 이번 포스트는 지난 시간에 이어 TAP에 대해서 더 깊이 있게 알아 볼 수 있는 시간을 가져 보도록 하겠습니다. 지난 포스트의 마지막 부분에서 외부 시스템에서 정보를 가져오는( I/O, Network, ...)을 통해 예제를 통해 비 동기로 실행하면 총 수행 시간을 절약할 수 있는 방안에 대해 알아 보았다. 이 번에는 .Net framework 4.5가 나오면서 C#이 5.0으로 업그레이드가 되었다. 그러면서 비 동기 컨트롤을 지원하는 키워드인 async/await가 추가 되어 더욱 편리하고 안전하게 사용할 수 있게 되었다. 이제 새로 추가된 기능으로 비 동기 구현을 해 보도록 하자. 

/// <summary>
/// async/await 키워드를 이용해서 비 동기 실행
/// </summary>
[TestMethod]
public async void TAP_AsyncAwait_TestMethod1()
{
    //비 동기 실행을 하지만 결과값이 리턴이 될 때까지 대기 한다.
    var returnValue = await Task.Factory.StartNew(() => 
        {
            Debug.WriteLine("TAP_AsyncAwait_TestMethod1 비 동기 실행 시작");
            Thread.Sleep(1000);
            Debug.WriteLine("TAP_AsyncAwait_TestMethod1 비 동기 실행 종료");
            return 10;
        });
 
    //정상 수행 여부 체크
    Assert.AreEqual(10returnValue);
}

[코드1] async/await로 비 동기 실행

(테스트 프로젝트에서는 정상적으로 실행 되지 않아 아래 Console에서 확인 한다)


class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        p.TAP_AsyncAwait_TestMethod1();
 
        Console.ReadKey();
    }
 
    public async void TAP_AsyncAwait_TestMethod1()
    {
        //비 동기 실행을 하지만 결과값이 리턴이 될 때까지 대기 한다.
        var returnValue = await Task.Factory.StartNew(() =>
        {
            Console.WriteLine("TAP_AsyncAwait_TestMethod1 비 동기 실행 시작");
            Thread.Sleep(1000);
            Console.WriteLine("TAP_AsyncAwait_TestMethod1 비 동기 실행 종료");
            return 10;
        });
 
        //값 확인
        Console.WriteLine(returnValue);
            
    

[코드2] Console에서 async/await로 비 동기 실행


 "코드2"에서와 같이 TAP_AsyncAwait_TestMethod 함수의 리턴 타입 앞에 'async' 키워드를 이용해서 이 함수는 비 동기로 수행 되는 메소드라고 알려줘야 한다 이렇게 해야지만 메소드 내부에서 await를 사용할 수 있다. 그리고 메소드 내부에서 비 동기로 수행되는 구문에서 await를 이용해서 결과값을 받아 오면 된다. 이렇게 간단한 규칙만으로 비 동기 실행을 절차적인 모양(기존 절차지향 언어에서 비 동기를 구현 할 때 Context를 맞춰줘야 하는 복잡한 지식 없이 절차 지향의 구문의 모양처럼 작성 하면서 메소드의 동작 방식은 비 동기로 구현 한다)으로 작성 하도록 도와 준다. 한번 직접 실행하여 분석해 보도록 하자. 생각 보다 쉽게 비 동기 지원 메소드를 작성 하고 사용할 수 있다. 


 이 기능은 프레임워크에서 지원되는 기능이 아닌 컴팔일러 단에서 지원하는 기능이며 내부적으로 TAP(1)에 설명된 사항 처럼 Task 객체를 이용해서 비 동기를 구현 하도록 내부적으로 구현 한다. .Net framework 4.5에서는 이렇게 쉽게 비 동기를 지원하는 편리한 기능을 추가 하였다. 그렇지만 모든 메소드를 비 동기로 작성해야 하는 필요성은 없으며 I/O, Network, DB와 같이 상대적으로 느린 시스템에서 데이터를 컨트롤 하는 부분에서만 비 동기를 사용할 것을 적극 권장하는 바이다.

Sysinternals Utilities Index


 MS Windows & Server 관련 프로그램을 개발 또는 운영(사용)하는데 있어서 유용하게 사용할 수 있는 유틸리티들이다. MS에서 제공하고 있으며, 아래 링크를 보면 여러가지 프로그램을 볼 수 있다.



http://technet.microsoft.com/en-us/sysinternals/bb545027 - Sysinternals utilities index

http://live.sysinternals.com/ - Download list

http://channel9.msdn.com/Shows/Defrag-Tools - Channel9 관련 동영상 모음



Sysinternals Suite
The entire set of Sysinternals Utilities rolled up into a single download.

AccessChk
v5.1 (August 2, 2012)
AccessChk is a command-line tool for viewing the effective permissions on files, registry keys, services, processes, kernel objects, and more.

AccessEnum
v1.32 (November 1, 2006)
This simple yet powerful security tool shows you who has what access to directories, files and Registry keys on your systems. Use it to find holes in your permissions.

AdExplorer
v1.44 (November 15, 2012)
Active Directory Explorer is an advanced Active Directory (AD) viewer and editor.

AdInsight
v1.01 (November 20, 2007)
An LDAP (Light-weight Directory Access Protocol) real-time monitoring tool aimed at troubleshooting Active Directory client applications.

AdRestore
v1.1 (November 1, 2006)
Undelete Server 2003 Active Directory objects.

Autologon
v3.01 (February 23, 2011)
Bypass password screen during logon.

Autoruns
v11.42 (January 31, 2013)
See what programs are configured to startup automatically when your system boots and you login. Autoruns also shows you the full list of Registry and file locations where applications can configure auto-start settings.

BgInfo
v4.16 (October 1, 2009)
This fully-configurable program automatically generates desktop backgrounds that include important information about the system including IP addresses, computer name, network adapters, and more.

BlueScreen
v3.2 (November 1, 2006)
This screen saver not only accurately simulates Blue Screens, but simulated reboots as well (complete with CHKDSK), and works on Windows NT 4, Windows 2000, Windows XP, Server 2003 and Windows 9x.

CacheSet
v1.0 (November 1, 2006)
CacheSet is a program that allows you to control the Cache Manager's working set size using functions provided by NT. It's compatible with all versions of NT.

ClockRes
v2.0 (June 4, 2009)
View the resolution of the system clock, which is also the maximum timer resolution.

Contig
v1.7 (November 15, 2012)
Wish you could quickly defragment your frequently used files? Use Contig to optimize individual files, or to create new files that are contiguous.

Coreinfo
v3.2 (November 15, 2012)
Coreinfo is a new command-line utility that shows you the mapping between logical processors and the physical processor, NUMA node, and socket on which they reside, as well as the cache’s assigned to each logical processor.

Ctrl2cap
v2.0 (November 1, 2006)
This is a kernel-mode driver that demonstrates keyboard input filtering just above the keyboard class driver in order to turn caps-locks into control keys. Filtering at this level allows conversion and hiding of keys before NT even "sees" them. Ctrl2cap also shows how to use NtDisplayString() to print messages to the initialization blue-screen.

DebugView
v4.81 (December 4, 2012)
Another first from Sysinternals: This program intercepts calls made to DbgPrint by device drivers and OutputDebugString made by Win32 programs. It allows for viewing and recording of debug session output on your local machine or across the Internet without an active debugger.

Desktops
v2.0 (October 17, 2012)
This new utility enables you to create up to four virtual desktops and to use a tray interface or hotkeys to preview what’s on each desktop and easily switch between them.

Disk2vhd
v1.63 (October 14, 2010)
Disk2vhd simplifies the migration of physical systems into virtual machines (p2v).

DiskExt
v1.1 (May 14, 2007)
Display volume disk-mappings.

Diskmon
v2.01 (November 1, 2006)
This utility captures all hard disk activity or acts like a software disk activity light in your system tray.

DiskView
v2.4 (March 25, 2010)
Graphical disk sector utility.

Disk Usage (DU)
v1.4 (December 5, 2011)
View disk usage by directory.

EFSDump
v1.02 (November 1, 2006)
View information for encrypted files.

FindLinks
v1.0 (July 18, 2011)
FindLinks reports the file index and any hard links (alternate file paths on the same volume) that exist for the specified file.  A file's data remains allocated so long as at it has at least one file name referencing it.

Handle
v3.51 (January 24, 2013)
This handy command-line utility will show you what files are open by which processes, and much more.

Hex2dec
v1.0 (November 1, 2006)
Convert hex numbers to decimal and vice versa.

Junction
v1.06 (September 8, 2010)
Create Win2K NTFS symbolic links.

LDMDump
v1.02 (November 1, 2006)
Dump the contents of the Logical Disk Manager's on-disk database, which describes the partitioning of Windows 2000 Dynamic disks.

ListDLLs
v3.1 (July 18, 2011)
List all the DLLs that are currently loaded, including where they are loaded and their version numbers.

LiveKd
v5.3 (October 17, 2012)
Use Microsoft kernel debuggers to examine a live system.

LoadOrder
v1.0 (November 1, 2006)
See the order in which devices are loaded on your WinNT/2K system.

LogonSessions
v1.21 (May 6, 2010)
List the active logon sessions on a system.

MoveFile
v1.01 (January 24, 2013)
Allows you to schedule move and delete commands for the next reboot.

NTFSInfo
v1.0 (November 1, 2006)
Use NTFSInfo to see detailed information about NTFS volumes, including the size and location of the Master File Table (MFT) and MFT-zone, as well as the sizes of the NTFS meta-data files.

PageDefrag
v2.32 (November 1, 2006)
Defragment your paging files and Registry hives.

PendMoves
v1.2 (February 5, 2013)
Enumerate the list of file rename and delete commands that will be executed the next boot.

PipeList
(November 1, 2006)
Displays the named pipes on your system, including the number of maximum instances and active instances for each pipe.

PortMon
v3.03 (January 12, 2012)
Monitor serial and parallel port activity with this advanced monitoring tool. It knows about all standard serial and parallel IOCTLs and even shows you a portion of the data being sent and received. Version 3.x has powerful new UI enhancements and advanced filtering capabilities.

ProcDump
v5.13 (January 24, 2013)
This command-line utility is aimed at capturing process dumps of otherwise difficult to isolate and reproduce CPU spikes. It also serves as a general process dump creation utility and can also monitor and generate process dumps when a process has a hung window or unhandled exception.

Process Explorer
v15.3 (February 5, 2013)
Find out what files, registry keys and other objects processes have open, which DLLs they have loaded, and more. This uniquely powerful utility will even show you who owns each process.

Process Monitor
v3.03 (July 16, 2012)
Monitor file system, Registry, process, thread and DLL activity in real-time.

PsExec
v1.98 (April 28, 2010)
Execute processes on remote systems.

PsFile
v1.02 (December 4, 2006)
See what files are opened remotely.

PsGetSid
v1.44 (April 28, 2010)
Displays the SID of a computer or a user.

PsInfo
v1.77 (April 28, 2010)
Obtain information about a system.

PsKill
v1.15 (June 28, 2012)
Terminate local or remote processes.

PsPing
v1.0 (October 3, 2012)
Measure network performance.

PsList
v1.3 (March 23, 2012)
Show information about processes and threads.

PsLoggedOn
v1.34 (April 28, 2010)
Show users logged on to a system.

PsLogList
v2.71 (April 28, 2010)
Dump event log records.

PsPasswd
v1.23 (October 17, 2012)
Changes account passwords.

PsService
v2.24 (April 28, 2010)
View and control services.

PsShutdown
v2.52 (December 4, 2006)
Shuts down and optionally reboots a computer.

PsSuspend
v1.06 (December 4, 2006)
Suspend and resume processes.

PsTools
(June 6, 2012)
The PsTools suite includes command-line utilities for listing the processes running on local or remote computers, running processes remotely, rebooting computers, dumping event logs, and more.

RAMMap
v1.21 (July 16, 2012)
An advanced physical memory usage analysis utility that presents usage information in different ways on its several different tabs.

RegDelNull
v1.10 (November 1, 2006)
Scan for and delete Registry keys that contain embedded null-characters that are otherwise undeleteable by standard Registry-editing tools.

RegJump
v1.01 (November 1, 2006)
Jump to the registry path you specify in Regedit.

RootkitRevealer
v1.71 (November 1, 2006)
Scan your system for rootkit-based malware.

SDelete
v1.61 (January 11, 2013)
Securely overwrite your sensitive files and cleanse your free space of previously deleted files using this DoD-compliant secure delete program.

ShareEnum
v1.6 (November 1, 2006)
Scan file shares on your network and view their security settings to close security holes.

ShellRunas
v1.01 (February 28, 2008)
Launch programs as a different user via a convenient shell context-menu entry.

Sigcheck
v1.91 (February 5, 2013)
Dump file version information and verify that images on your system are digitally signed.

Streams
v1.56 (April 27, 2007)
Reveal NTFS alternate streams.

Strings
v2.5 (May 14, 2012)
Search for ANSI and UNICODE strings in binary images.

Sync
v2.0 (November 1, 2006)
Flush cached data to disk.

TCPView
v3.05 (July 25, 2011)
Active socket command-line viewer.

VMMap
v3.11 (September 10, 2012)
VMMap is a process virtual and physical memory analysis utility.

VolumeId
v2.0 (November 1, 2006)
Set Volume ID of FAT or NTFS drives.

Whois
v1.11 (October 17, 2012)
See who owns an Internet address.

WinObj
v2.22 (February 14, 2011)
The ultimate Object Manager namespace viewer is here.

ZoomIt
v4.42 (February 5, 2013)
Presentation utility for zooming and drawing on the screen.


 다음 포스트에서는 위 프로그램중에 유용한 유틸리티들을 살펴 보도록 하겠다.

 .Net framework 기반으로 개발 또는 운영(사용) 할 때 유용하게 사용할 수 있는 유틸리티들이다.(.Net Framework에 한정되어 있는건 아니다) 어플리케이션의 메모리 구조를 분석할 수 있는 툴, 프로세서의 상황을 분석할 수 있는 툴등... 여러가지 유티리티들을 MS에서 제공하고 있다. 다음 링크를 따라가서 하나 하나씩 살펴 보도록 하자. 


 아래는 시스템 정보에 관계된 유틸리티다.


Sysinternals System Information Utilities 
http://technet.microsoft.com/en-us/sysinternals/bb795535


Autoruns
See what programs are configured to startup automatically when your system boots and you login. Autoruns also shows you the full list of Registry and file locations where applications can configure auto-start settings.

ClockRes
View the resolution of the system clock, which is also the maximum timer resolution.

Coreinfo
Coreinfo is a command-line utility that shows you the mapping between logical processors and the physical processor, NUMA node, and socket on which they reside, as well as the cache’s assigned to each logical processor.

Handle
This handy command-line utility will show you what files are open by which processes, and much more.

LiveKd
Use Microsoft kernel debuggers to examine a live system.

LoadOrder
See the order in which devices are loaded on your WinNT/2K system.

LogonSessions
List the active logon sessions on a system.

PendMoves
Enumerate the list of file rename and delete commands that will be executed the next boot.

Process Explorer
Find out what files, registry keys and other objects processes have open, which DLLs they have loaded, and more. This uniquely powerful utility will even show you who owns each process.

Process Monitor
Monitor file system, Registry, process, thread and DLL activity in real-time.

ProcFeatures
This applet reports processor and Windows support for Physical Address Extensions and No Execute buffer overflow protection.

PsInfo
Obtain information about a system.

PsLoggedOn
Show users logged on to a system

PsTools
The PsTools suite includes command-line utilities for listing the processes running on local or remote computers, running processes remotely, rebooting computers, dumping event logs, and more.

RAMMap
An advanced physical memory usage analysis utility that presents usage information in different ways on its several different tabs.

WinObj
The ultimate Object Manager namespace viewer is here.


 다음 포스트에서는 MS에서 제공하는 유틸리티들을 하나 하나씩 살펴 보도록 하겠다. 위 유틸리티를 설명하는 Channel9의 영상 링크를 먼저 알려 드리도록 하겠다. 

http://channel9.msdn.com/Shows/Defrag-Tools

http://technet.microsoft.com/en-us/sysinternals/bb795535







 .Net framework 기반으로 개발 또는 운영(사용) 할 때 유용하게 사용할 수 있는 유틸리티들이다.(.Net Framework에 한정되어 있는건 아니다) 어플리케이션의 메모리 구조를 분석할 수 있는 툴, 프로세서의 상황을 분석할 수 있는 툴등... 여러가지 유티리티들을 MS에서 제공하고 있다. 다음 링크를 따라가서 하나 하나씩 살펴 보도록 하자. 


 아래는 프로세스 관련 유틸리티다.


Sysinternals Process Utilities 
http://technet.microsoft.com/en-us/sysinternals/bb795533


Autoruns
See what programs are configured to startup automatically when your system boots and you login. Autoruns also shows you the full list of Registry and file locations where applications can configure auto-start settings.

Handle
This handy command-line utility will show you what files are open by which processes, and much more.

ListDLLs
List all the DLLs that are currently loaded, including where they are loaded and their version numbers. Version 2.0 prints the full path names of loaded modules.

PortMon
Monitor serial and parallel port activity with this advanced monitoring tool. It knows about all standard serial and parallel IOCTLs and even shows you a portion of the data being sent and received. Version 3.x has powerful new UI enhancements and advanced filtering capabilities.

ProcDump
This new command-line utility is aimed at capturing process dumps of otherwise difficult to isolate and reproduce CPU spikes. It also serves as a general process dump creation utility and can also monitor and generate process dumps when a process has a hung window or unhandled exception.

Process Explorer
Find out what files, registry keys and other objects processes have open, which DLLs they have loaded, and more. This uniquely powerful utility will even show you who owns each process.

Process Monitor
Monitor file system, Registry, process, thread and DLL activity in real-time.

PsExec
Execute processes remotely.

PsGetSid
Displays the SID of a computer or a user.

PsKill
Terminate local or remote processes.

PsList
Show information about processes and threads.

PsService
View and control services.

PsSuspend
Suspend and resume processes.

PsTools
The PsTools suite includes command-line utilities for listing the processes running on local or remote computers, running processes remotely, rebooting computers, dumping event logs, and more.

ShellRunas
Launch programs as a different user via a convenient shell context-menu entry.

VMMap
See a breakdown of a process's committed virtual memory types as well as the amount of physical memory (working set) assigned by the operating system to those types. Identify the sources of process memory usage and the memory cost of application features.


 다음 포스트에서는 MS에서 제공하는 유틸리티들을 하나 하나씩 살펴 보도록 하겠다. 위 유틸리티를 설명하는 Channel9의 영상 링크를 먼저 알려 드리도록 하겠다. 

http://channel9.msdn.com/Shows/Defrag-Tools

http://technet.microsoft.com/en-us/sysinternals/bb795535

C# SemaphoreSlim


2013/02/27 - [.Net Framework] - [Threading] Semaphore(세마포어) - C#



 지난 포스트에서 소개 했던 세마포어(Seamphore) 대신에 Windows 커널 세마포어를 사용하지 않는 간단한 클래스이다. 사용목적은 세마포어와 같으며 사용방법은 다음 코드를 보자

//한번에 허용할 수 있는 최대 쓰레드 수
private static int _maximumThreads = 3;
 
/// <summary>
/// SemaphoreSlim으로 동시 쓰레드 갯수 제한 테스트
/// </summary>
[TestMethod]
public void SemaphoreSlim_TestMethod()
{
    SemaphoreSlim ss = new SemaphoreSlim(0_maximumThreads);
 
    for (int i = 0i <= 5i++)
    {
        Thread thread = new Thread(new ThreadStart(() =>
        {
            ss.Wait();
            Thread.Sleep(100);
            Debug.WriteLine("{0} 실행 됨."Thread.CurrentThread.Name);
            ss.Release();
        }));
 
        thread.Name = String.Concat("Thread "i + 1);
        thread.Start();
    }
 
 
    Thread.Sleep(300);
    ss.Release(_maximumThreads);
 
    ss.AvailableWaitHandle.WaitOne();
    Debug.WriteLine("ss.CurrentCount after ss.Wait() = {0}"ss.CurrentCount);
}

[코드1] SemaphoreSlim으로 동시 쓰레드 실행 제한


 위 코드의 자세한 사항은 바로 이전 포스트를 보면 알 수 있을 것이다.





+ Recent posts