C#에서 Thread내에 Parallel.Foreach 가 있는 경우 중단 방법
January 09, 2017
dotnet
GUI 환경에서 버튼을 클릭하는 등의 이벤트로 시간이 오래 걸리는 작업을 구동하는 경우 스레드를 분리하여 개발하는 방법이 좋다는 방법은 C# 뿐만 아니라 안드로이드나 MFC 등 GUI를 어느 정도 개발한 사람이라면 익숙하리라 생각한다.
그렇다면 그 시간이 오래 걸리는 작업을 더욱더 빠르게 하고 싶다면 병렬 처리가 가장 쉽고 빨리 적용할 수 있는 합리적인 방법이라고 생각한다.
구글링을 통하여 어찌어찌 병렬 For 문을 적용하였지만, 문제는 “클라이언트가 구동은 했지만 오래 걸리는 프로세스를 작업 도중에 중단하고 싶다”라고 말했을 때 발생하였다.
다음은 몇 가지 코드를 조합하여 만든 메인 스레드와 분리된 다중 포문 작업 클래스와 그 작업을 중단시키는 예를 콘솔 응용프로그램으로 구성하였다. 1
메인 클래스 및 중단 클래스
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ThreadTest
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("press any key for Thread start!");
Console.ReadKey();
RulyCanceler canceler = new RulyCanceler();
TestFilter tf = new TestFilter(10000000);
Thread t = new Thread(() =>
{
try
{
tf.execute(canceler);
}catch(OperationCanceledException)
{
Console.WriteLine("Canceled!");
}
});
t.Start();
Console.ReadKey();
Console.WriteLine("Abort!");
canceler.Cancel();
t.Abort();
t.Join();
Console.WriteLine("Aborted!");
Console.ReadKey();
}
}
public class RulyCanceler
{
object _cancelLocker = new object();
bool _cancelRequest;
public bool IsCancellationRequested
{
get { lock (_cancelLocker) return _cancelRequest; }
}
public void Cancel() { lock (_cancelLocker) _cancelRequest = true; }
public void ThrowIfCancellationRequested()
{
if (IsCancellationRequested) throw new OperationCanceledException();
}
}
}
시간이 오래걸리는 작업 클래스
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ThreadTest
{
public class TestFilter
{
private long featureCount;
private int MULTI_THREAD_COUNT = 10;
EventWaitHandle waitEvent = new EventWaitHandle(true, EventResetMode.AutoReset, "SHARED_BY_ALL_PROCESSES");
public TestFilter(long featurecount)
{
this.featureCount = featurecount;
}
public void execute(RulyCanceler cancer)
{
int workingRangeSize = 1;
if (featureCount > 100)
{
workingRangeSize = (int)(featureCount / MULTI_THREAD_COUNT);
}
var part = Partitioner.Create(0, featureCount, workingRangeSize);
Parallel.ForEach(part, (num, state) =>
{
for (long featureIdx = num.Item1, cnt = num.Item2; featureIdx < cnt; featureIdx++)
{
try
{
//waitEvent.WaitOne();(파일 쓰기모드일 경우 하나의 스레드만 쓸수 있도록 줄세우기)
//waitEvent.Set();
// 중단 요청이있으면 Throw
cancer.ThrowIfCancellationRequested();
// 실제 일
Console.WriteLine(String.Format("{0} - {1}", num.Item1, featureIdx));
}
catch (OperationCanceledException ex)
{
Console.WriteLine("Breaked.");
Console.WriteLine("Clean");
state.Break();
break;
}
}
});
}
}
}