C# - TPL
닷넷 프레임워크 4.0에서 추가된 병렬 프로그래밍 라이브러리
여기의 글을 정리
Parallel.ForEach/For
- Parallel.ForEach
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace Sample01_ParallelForEach
{
class Program
{
static void Main()
{
//----- 데이터 생성
var collection = new int[]{ 10, 20, 30, 40, 50 };
//----- 일반적인 루프
var stopwatch = Stopwatch.StartNew();
foreach (var item in collection)
{
Console.WriteLine(item);
Thread.Sleep(1000);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds.ToString("Normal : 0[ms]"));
//----- 병렬 루프
stopwatch.Restart();
Parallel.ForEach(collection, item =>
{
Console.WriteLine(item);
Thread.Sleep(1000);
});
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds.ToString("Parallel : 0[ms]"));
}
}
}
//----- 결과 : 환경에 따라서 다소 결과가 다른 경우도 있다. 10 20 30 40 50 Normal : 5002[ms] 10 20 40 30 50 Parallel : 1010[ms]
- Parallel.For
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace Sample02_ParallelFor
{
class Program
{
static void Main()
{
//----- 데이터 생성
var collection = new int[]{ 10, 20, 30, 40, 50 };
//----- 일반적인 루프
var stopwatch = Stopwatch.StartNew();
for (int index = 0; index < collection.Length; index++)
{
Console.WriteLine(collection[index]);
Thread.Sleep(1000);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds.ToString("Normal : 0[ms]"));
//----- 병렬 루프
stopwatch.Restart();
Parallel.For(0, collection.Length, index =>
{
Console.WriteLine(collection[index]);
Thread.Sleep(1000);
});
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds.ToString("Parallel : 0[ms]"));
}
}
}
10 20 30 40 50 Normal : 5002[ms] 10 30 50 20 40
Parallel 루프 중단
- Paralle.For/ForEach 는 기본적으로 루프 중에 break 문으로 중단할 수 없다.
- ParallelLoopState.Stop
- Stop 이 호출될 때 실행 중인 작업이 중단된다.
- 다른 스레드에서 Stop 호출을 확인하기 위해서 ParallelLoopState.IsStopped 를 사용한다.
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace Sample04_StopLoop
{
class Program
{
static void Main()
{
var stack = new ConcurrentStack<long>();
Parallel.For(0, 100000, (index, state) =>
{
if (index < 50000) stack.Push(index);
else state.Stop();
});
Console.WriteLine("개수: " + stack.Count);
}
}
}
- ParallelLoopState.Break
- 작업 중 Break가 호출되면 현재 작업 중인 것은 처리하고 다음부터 중단된다.
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace Sample05_BreakLoop
{
class Program
{
static void Main()
{
var stack = new ConcurrentStack<long>();
Parallel.For(0, 100000, (index, state) =>
{
stack.Push(index);
if (index >= 50000)
{
state.Break();
Console.WriteLine("Index : " + index);
Console.WriteLine("LowestBreakIteration : " + state.LowestBreakIteration);
}
});
Console.WriteLine("개수 : " + stack.Count);
}
}
}
결과 ndex : 50000 LowestBreakIteration : 50000 Index : 50016 LowestBreakIteration : 50000 개수 : 50002
- ParallelLoopResult
- 병렬 루프가 작업 도중 정지된 것인지 알 수 있다.
using System.Threading.Tasks;
namespace Sample06_LoopResult
{
class Program
{
static void Main()
{
var result = Parallel.For(0, 100000, (index, state) =>
{
//----- Do Something...
});
if (result.IsCompleted)
{
//----- 모든 루프가 실행 되었다
}
else if (result.LowestBreakIteration.HasValue)
{
//----- Break 되었다
}
else
{
//----- Stop 되었다
}
}
}
}
스레드 로컬 변수
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Sample07_ThreadLocalVariables
{
class Program
{
static void Main()
{
int total = 0;
Parallel.ForEach
(
Enumerable.Range(1, 100), // 병렬 처리를 할 소스
() => 0, //스레드 로컬 변수 초기화 값
(value, state, local) => local += value, // 루프 작업
local => Interlocked.Add(ref total, local) // 루프 작업 완료 후 처리
);
Console.WriteLine(total);
}
}
}
예외 처리
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Sample08_LoopException
{
class Program
{
static void Main()
{
try
{
Parallel.ForEach(Enumerable.Range(0, 100), value =>
{
if (value % 2== 0) throw new InvalidOperationException();
else throw new ArgumentException();
});
}
catch (AggregateException ex)
{
foreach (var exception in ex.InnerExceptions)
Console.WriteLine(exception.GetType().Name);
}
}
}
}
결과 InvalidOperationException InvalidOperationException ArgumentException ArgumentException InvalidOperationException InvalidOperationException ArgumentException ArgumentException InvalidOperationException
-내부에서 발생한 예외는 AggregateException의 InnerExceptions 프로퍼티에 모두 저장된다. 이후 catch 문에서 각 예외에 따른 적당한 처리를 하면 된다.
루프 취소
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Sample09_CancelLoop
{
class Program
{
static void Main()
{
var source = new CancellationTokenSource();
var thread = new Thread(DoCancelableAction);
thread.Start(source.Token);
if (Console.ReadKey().Key == ConsoleKey.C)
{
Console.Clear();
Console.WriteLine("press cancel key");
source.Cancel();
}
}
static void DoCancelableAction(object parameter)
{
try
{
var option = new ParallelOptions(){ CancellationToken = (CancellationToken)parameter };
Parallel.ForEach(Enumerable.Range(0, 1000), option, value =>
{
Thread.Sleep(100);
option.CancellationToken.ThrowIfCancellationRequested();
});
}
catch (AggregateException)
{
//----- 병렬처리 중에서 예외지만 여기에는 들어오지 않는다
//----- 취소에 의한 예외는 다르게 처리한다
}
catch (OperationCanceledException e)
{
Console.WriteLine(e.Message);
}
}
}
}
- CancellationTokenSource는 예외를 처리하기 위한 오브젝트로 이것을 생성한 토큰에 대해서 취소를 요구할 수 있다.
- ThrowIfCancellationRequested는 아래와 코드와 비슷하므로 호출에 따른 오버헤드가 크지 않다.
if (token.IsCancellationRequested)
throw new OperationCanceledException(token);
- 위 코드의 취소는 수동적 방법으로 이 이외에 콜백이나 대기 핸들을 사용하는 방법도 있다.
- http://msdn.microsoft.com/ko-kr/library/dd997364.aspx
태스크 실행
- 생성과 실행 분리
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Sample10_TaskStart
{
class Program
{
static void Main()
{
var task1 = new Task(() => Console.WriteLine("Task1"));
var task2 = new Task(() => Console.WriteLine("Task2"));
var task3 = new Task(() => Console.WriteLine("Task3"));
task1.Start();
task2.Start();
task3.Start();
Thread.Sleep(1000);
}
}
}
- 위의 코드는 Task 생성 시 생성자 인수로서 비동기로 처리를 델리게이트로 기술하고, 생성된 인스턴스의 Start 매소도를 호출하여 처리를 시작한다.
- Start 메소드는 호출한 스레드를 블럭하지 않는다.
- 생성과 동시에 실행
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Sample11_TaskFactoryStartNew
{
class Program
{
static void Main()
{
var task1 = Task.Factory.StartNew(() => Console.WriteLine("Task1"));
var task2 = Task.Factory.StartNew(() => Console.WriteLine("Task2"));
var task3 = Task.Factory.StartNew(() => Console.WriteLine("Task3"));
Thread.Sleep(1000);
}
}
}
- 암묵적인 태스크
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Sample12_ParallelInvoke
{
class Program
{
static void Main()
{
Console.WriteLine(DebugMessage("Begin"));
Parallel.Invoke
(
() =>
{
Thread.Sleep(1000);
Console.WriteLine(DebugMessage("Task1"));
},
() =>
{
Thread.Sleep(3000);
Console.WriteLine(DebugMessage("Task2"));
},
() =>
{
Thread.Sleep(5000);
Console.WriteLine(DebugMessage("Task3"));
}
);
Console.WriteLine(DebugMessage("End"));
}
static string DebugMessage(string keyword)
{
string format = "{0}\n\tTime = {1}\n\tThread ID = {2}\n";
return string.Format(format, keyword, DateTime.Now.ToLongTimeString(), Thread.CurrentThread.ManagedThreadId);
}
}
}
- Invoke는 인수로 받은 작업을 병렬로 모두 처리할 때까지 호출한 스레드가 블럭된다.
완료 대기와 결과 취득
- 시작된 Task가 완료될 때까지 기다리기 위해서는 Wait 메소드를 호출한다. 태스크가 종료될 때까지 호출한 스레드가 블럭된다.
using System;
using System.Threading.Tasks;
namespace Sample13_TaskWait
{
class Program
{
static void Main()
{
Console.WriteLine("Begin");
var task = new Task(() => Console.WriteLine("Task is running"));
task.Start();
task.Wait();
Console.WriteLine("End");
}
}
}
- 복수의 태스크의 완료를 대기하고 싶을 때에는 WaitAll을 사용한다.
- 대기 시간에 타임오버를 설정할 수 있으며, 설정한 경우 시간내에 모두 완료되면 true를 반환한다.
using System;
using System.Threading.Tasks;
namespace Sample14_TaskWaitAll
{
class Program
{
static void Main()
{
Console.WriteLine("Begin");
var tasks = new []
{
Task.Factory.StartNew(() => Console.WriteLine("Task1 is running")),
Task.Factory.StartNew(() => Console.WriteLine("Task2 is running")),
Task.Factory.StartNew(() => Console.WriteLine("Task3 is running")),
};
Task.WaitAll(tasks);
Console.WriteLine("End");
}
}
}
- 복수의 태스크 중 어느 하나가 완료될 때까지만 대기하고 싶은 경우는 WaitAny를 사용한다.
- 타임아웃 설정이 가능하고, 타임아웃이 발생한 경우 -1을 반환한다.
using System;
using System.Threading.Tasks;
namespace Sample15_TaskWaitAny
{
class Program
{
static void Main()
{
Console.WriteLine("Begin");
var tasks = new []
{
Task.Factory.StartNew(() => Console.WriteLine("Task1 is running")),
Task.Factory.StartNew(() => Console.WriteLine("Task2 is running")),
Task.Factory.StartNew(() => Console.WriteLine("Task3 is running")),
};
int index = Task.WaitAny(tasks);
Console.WriteLine("Index = {0}", index);
Console.WriteLine("End");
}
}
}
- 결과 얻기
- Task
클래스를 이용한다. - Result 메소드는 내부적으로 Wait 메소드를 호출한다.
- Task
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Sample16_TaskResult
{
class Program
{
static void Main()
{
Console.WriteLine("Begin");
var task = new Task<int>(() => Enumerable.Range(1, 10).Sum());
task.Start();
task.Wait(); //--- なくても良い
Console.WriteLine("Sum = {0}", task.Result);
Console.WriteLine("End");
}
}
}
태스크 순차 실행
- 태스크1과 태스크2를 순차적으로 실행하고 싶은 경우 ContinueWith 메소드를 사용한다.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Sample17_TaskContinueWith
{
class Program
{
static void Main()
{
Console.WriteLine("Main : Begin");
var task = new Task<int>(() =>
{
Console.WriteLine("Task1 : Begin");
var sum = Enumerable.Range(1, 10000).Sum();
Console.WriteLine("Task1 : End");
return sum;
});
task.Start();
task.ContinueWith(task1 =>
{
Console.WriteLine("Task2 : Begin");
Console.WriteLine("Sum = {0}", task1.Result);
Console.WriteLine("Task2 : End");
});
Console.WriteLine("Main : End");
Thread.Sleep(1000);
}
}
}
결과 Main : Begin Main : End Task1 : Begin Task1 : End Task2 : Begin Sum = 50005000 Task2 : End
- 위의 두 개의 태스크가 완료될 때까지 이것들을 호출한 스레드는 블럭되지 않는다. 그래서 위의 코드에서는 의도적으로 1초정도 대기를 하고 있다.
- ContinueWith를 사용하면 앞에 것이 완료된 후 다음 것이 시작되는 것을 보장한다.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Sample18_TaskContinueWithMultiple
{
class Program
{
static void Main()
{
Console.WriteLine("Begin");
var task = new Task(() => Console.WriteLine("Task1"));
task.Start();
task.ContinueWith(task1 => Console.WriteLine("Task2"));
task.ContinueWith(task1 => Console.WriteLine("Task3"));
Console.WriteLine("End");
Thread.Sleep(1000);
}
}
}
결과 Begin End Task1 Task3 Task2
- 옵션
옵션 설명 OnlyOnRanToCompletion 앞의 태스크가 완료까지 실행된 경우에만 다음 태스크를 스케쥴한다. OnlyOnFaulted 앞의 태스크에서 예외가 핸들링 된 경우에만 다음 태스크를 스케줄한다. OnlyOnCanceled 앞의 태스크가 취소된 경우에만 다음 태스크를 스케줄한다.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Sample19_TaskContinuationOptions
{
class Program
{
static void Main()
{
Console.WriteLine("Begin");
var task = Task<int>.Factory.StartNew(() => Enumerable.Range(1, 10000).Sum());
task.ContinueWith
(
task1 => Console.WriteLine("Success : {0}", task1.Result),
TaskContinuationOptions.OnlyOnRanToCompletion
);
task.ContinueWith
(
task1 => Console.WriteLine("Error : {0}", task1.Exception),
TaskContinuationOptions.OnlyOnFaulted
);
task.ContinueWith
(
task1 => Console.WriteLine("Task was canceled."),
TaskContinuationOptions.OnlyOnCanceled
);
Console.WriteLine("End");
Thread.Sleep(1000);
}
}
}
결과 Begin End Success : 50005000
태스크 중첩과 자식 태스크
- 중첩 태스크
- 태스크 A상의 스레드와 다른 스레드에서 태스크 B가 실행된다.
- 태스크 B를 대기시키는 코드를 명시하지 않으면 태스크 A는 태스크 B의 완료를 기다리지 않고 종료한다.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Sample20_NestedTask
{
class Program
{
static void Main()
{
Console.WriteLine("Main : Begin");
var task1 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Task1 : Begin");
var task2 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Task2 : Begin");
Thread.Sleep(3000);
Console.WriteLine("Task2 : End");
});
//task2.Wait(); //--- コメントの有無で結果が変わります
Console.WriteLine("Task1 : End");
});
task1.Wait();
Console.WriteLine("Main : End");
}
}
}
//----- 결과 (Task2의 대기 없음) Main : Begin Task1 : Begin Task1 : End Main : End Task2 : Begin //----- 결과 (Task2의 대기 있음) Main : Begin Task1 : Begin Task2 : Begin Task2 : End Task1 : End Main : End
- 부모자식 관계를 가진 태스크
- 채스크에 부모자식 관계를 가지기 위해서는 태스크 생성 시에 TaskCreationOptions.AttachedToParent 를 지정한다.
- 태스크가 실행을 완료할 때까지 부모가 완료된 이후에 자식이 완료된다.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Sample21_ParentChildTask
{
class Program
{
static void Main()
{
Console.WriteLine("Main : Begin");
var task1 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Task1 : Begin");
Task.Factory.StartNew(() =>
{
Console.WriteLine("Task2 : Begin");
Thread.Sleep(3000);
Console.WriteLine("Task2 : End");
}, TaskCreationOptions.AttachedToParent);
Console.WriteLine("Task1 : End");
});
task1.Wait();
Console.WriteLine("Main : End");
}
}
}
Main : Begin Task1 : Begin Task1 : End Task2 : Begin Task2 : End Main : End
태스크의 예외 처리
- 예외 포착
- 태스크 중에서 처리되지 않은 예외는 태스크 자신이 포착하여 컬렉션으로 보존한다.
- Wait 혹은 Result 프로퍼티가 실행되면 이 멤버들에서 System.AggregateException가 스로우된다.
- 태스크가 포착한 예외는 스로우된 AggregateException의 InnerExceptions 프로퍼티에서 취득할 수 있다.
using System;
using System.Threading.Tasks;
namespace Sample22_TaskException
{
class Program
{
static void Main()
{
var task = Task.Factory.StartNew(() =>
{
throw new Exception("Test Exception");
});
try
{
task.Wait();
}
catch (AggregateException exception)
{
foreach (var inner in exception.InnerExceptions)
Console.WriteLine(inner.Message);
}
}
}
}
결과 Test Exception
- 자식 태스크에서 발생한 예외 처리
using System;
using System.Threading.Tasks;
namespace Sample23_NestedException
{
class Program
{
static void Main()
{
var task = Task.Factory.StartNew(() =>
{
Task.Factory.StartNew(() =>
{
throw new Exception("Task2 : Exception");
}, TaskCreationOptions.AttachedToParent);
throw new InvalidOperationException("Task1 : Exception");
});
try
{
task.Wait();
}
catch (AggregateException exception)
{
foreach (var inner in exception.InnerExceptions)
{
Console.WriteLine(inner.Message);
Console.WriteLine("Type : {0}", inner.GetType());
}
}
}
}
}
결과 Task1 : Exception Type : System.InvalidOperationException 하나 이상의 에러가 발생. Type : System.AggregateException
- 자식 태스크에서 발생한 예외 검출시에 Flatten 메소드 사용
using System;
using System.Threading.Tasks;
namespace Sample24_ExceptionFlatten
{
class Program
{
static void Main()
{
var task = Task.Factory.StartNew(() =>
{
Task.Factory.StartNew(() =>
{
throw new Exception("Task2 : Exception");
}, TaskCreationOptions.AttachedToParent);
throw new InvalidOperationException("Task1 : Exception");
});
try
{
task.Wait();
}
catch (AggregateException exception)
{
foreach (var inner in exception.Flatten().InnerExceptions)
{
Console.WriteLine(inner.Message);
Console.WriteLine("Type : {0}", inner.GetType());
}
}
}
}
}
결과 Task1 : Exception Type : System.InvalidOperationException Task2 : Exception Type : System.Exception
- 미처리 예외
- 아래의 상황에서 예외가 발생하면 확인할 수 없다.
- Task.Wait 메소드를 호출 하지 않는다.
- Task
.Result 프로퍼티를 호출 하지 않는다. - Task.Exception 프로퍼티를 호출하지 않는다.
- 그러나 방법은 있다. 태스크 인스턴스가 가베지 컬렉션에 의해 회수될 때 Task.Finalize 메소드는 자신의 예외가 확인되고 있는지 확인한다. 확인되지 않았다고 판단된 경우 Finalize 메소드에서 AggregateException가 스로우된다. Finalize 메소드는 CLR 전용 스레드인 Finalizer 스레드 상에서 실행되기 때문에 그 예외는 포착할수 없고 프로세스가 즉각 종료된다.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Sample25_UnobservedTaskException
{
class Program
{
static void Main()
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
Task.Factory.StartNew(() => { throw new InvalidOperationException("Task1"); });
Task.Factory.StartNew(() => { throw new InvalidCastException("Task2"); });
Thread.Sleep(300); //--- 태스크 종류를 대기
GC.Collect(); //--- 태스크 인스턴스를 회수
GC.WaitForPendingFinalizers(); //--- Finalize를 강제적으로 호출한다
Console.WriteLine("End");
}
static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
foreach (var inner in e.Exception.Flatten().InnerExceptions)
{
Console.WriteLine(inner.Message);
Console.WriteLine("Type : {0}", inner.GetType());
}
e.SetObserved(); //--- 処理済みとしてマークする
}
}
}
결과 Task1 Type : System.InvalidOperationException Task2 Type : System.InvalidCastException End
태스크 취소
- 시작과 동시에 취소
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Sample26_CancelTaskStart
{
class Program
{
static void Main()
{
var source = new CancellationTokenSource();
var task = new Task(() => Console.WriteLine("태스크가 실행 되었다."), source.Token);
task.Start();
source.Cancel();
try
{
task.Wait();
}
catch (AggregateException exception)
{
foreach (var inner in exception.InnerExceptions)
{
Console.WriteLine(inner.Message);
Console.WriteLine("Type : {0}", inner.GetType());
}
}
}
}
}
결과 태스크가 취소 되었다. Type : System.Threading.Tasks.TaskCanceledException
- 태스크의 시작과 취소는 비동기로 실행되기 때문의 위의 코드는 시작도 못하고 바로 취소 되었다.
- 만약 시작 이후 취소까지 대기 타임을 주면 취소 되지 않고 실행된다. - 실행 중에 취소
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Sample27_CancelTaskByPolling
{
class Program
{
static void Main()
{
var source = new CancellationTokenSource();
var token = source.Token;
var task = new Task(() =>
{
Console.WriteLine("Task : Begin");
foreach (var item in Enumerable.Range(0, 10))
{
token.ThrowIfCancellationRequested();
Thread.Sleep(100);
}
Console.WriteLine("Task : End");
});
task.Start();
Thread.Sleep(300); //--- 태스크가 실행되도록 한다.
source.Cancel();
try
{
task.Wait();
}
catch (AggregateException exception)
{
foreach (var inner in exception.InnerExceptions)
{
Console.WriteLine(inner.Message);
Console.WriteLine("Type : {0}", inner.GetType());
}
}
}
}
}
결과 Task : Begin 조작은 취소 되었다. Type : System.OperationCanceledException
UI 컴포넌트 조작
- TPL에는 스케쥴이라는 개념이 있으며 이것이 태스크의 실행 순서를 정하고 스레드로 실행되도록 한다.TPL 표준에서는 스레드 풀 태스크 스케쥴러와 동기 컨텍스트 스케쥴러가 있다.
- TPL의 기본은 스레드 풀 스케쥴러 이며 정적인 TaskScheduler.Default 프로퍼티에서 얻을 수 있다. 스레드 풀 스케줄러는 그 이름대로 태스크를 스레드 풀의 워커스레드로 등록하여 처리한다.
- 동기 컨텍스트 스케쥴러는 정적인 TaskScheduler.FromCurrentSynchronizationContext 메소드에서 얻을 수 있다. 이 스케쥴러는 Windows Forms나 WPF 등의 GUI 애플리케이션에 주로 사용되며, 버턴이나 메뉴 등의 UI 컴포넌트를 태스크 상에서 갱신하도록 태스크를 애플리케이션 UI 스레드에 등록하여 처리하도록 한다. 스레드 풀은 일정 사용할 수 없다.
- 동기 컨텍스트 스케쥴러 사용
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Sample28_SynchronizationContextTaskScheduler
{
public partial class MainForm : Form
{
public MainForm()
{
this.InitializeComponent();
this.button.Click += this.Button_Click;
}
private void Button_Click(object sender, EventArgs e)
{
this.button.Enabled = false;
Task.Factory.StartNew(() =>
{
Thread.Sleep(3000); //---- Do Somothing
})
.ContinueWith(parent =>
{
this.button.Enabled = true;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
}
TaskFactory.FromAsync
- 출처: http://kyeongkyun.tistory.com/85
- IAsyncResult 패턴을 Task로 변환 할 때 사용하면 좋다
- IAsyncResult 기반의 비동기 연산인 Dns.BeginGtHostAddress를 Task로 변환
static void Main() {
Task<IPAddress[]> task =
Task<IPAddress[]>.Factory.FromAsync(
Dns.BeginGetHostAddresses,
Dns.EndGetHostAddresses,
"http://www.microsoft.com", null);
...
}
- 내부적으로 FromAsync 메서드는 스레드 풀을 활용하는 TaskCompletionSource의 예제와 비슷한 방법으로 구현
static Task<IPAddress[]> GetHostAddressesAsTask(
string hostNameOrAddress) {
var tcs = new TaskCompletionSource<IPAddress[]>();
Dns.BeginGetHostAddresses(hostNameOrAddress, iar => {
try {
tcs.SetResult(Dns.EndGetHostAddresses(iar)); }
catch(Exception exc) { tcs.SetException(exc); }
}, null);
return tcs.Task;
}
- (일본어)TPL과 기존 .NET 비동기 프로그래밍
- http://msdn.microsoft.com/ja-jp/library/vstudio/dd997423.aspx
Task.Run
- 닷넷 프레임워크 4.5 에서 추가된 것
//.Net framework 4.5에 추가된 메소드로
//Task.Factory.StartNew 래핑한다.
Task.Run(() => { /* Something */ });
- http://csharp.keicode.com/basic/async-how-to-write.php
복수의 task를 동시에 실행. Task.WhenAll(async/await), Task.WaitAll
await Task.WhenAll( redisObj.SetField("ID", userData.ID),
redisObj.SetField("PW", userData.PW),
redisObj.SetField("AuthToken", userData.AuthToken),
redisObj.SetField("UnqiueNumber", userData.UnqiueNumber) );
Task.WaitAll( redisObj.SetField("ID", userData.ID),
redisObj.SetField("PW", userData.PW),
redisObj.SetField("AuthToken", userData.AuthToken),
redisObj.SetField("UnqiueNumber", userData.UnqiueNumber) );
이 글은 2019-03-10에 작성되었습니다.