본 자료는
1. Worker Thread와 UI Thread의 차이점
2. Work Thread와 UI Thread의 장점만 사용하는 방법
3. Work Thread와 UI Thread의 조합에 따른 활용 방법
4. 활용 예제 프로그램 소스 코드
5. 활용 예제 프로그램 사용 방법 및 결과
순서로 진행합니다.
SmartThread는 두 가지의 Thread를 지원하며 아래 표와 같은 장단점이 있습니다.
[표] Work Thread와 UI Thread 비교Thread | Work Thread | UI Thread |
---|---|---|
연산 처리 속도 | 빠름 | 느림 |
UI 접근 | 불가능 | 가능 |
관련 이벤트 | ThreadFunction | UIThreadFunction |
SmartThread는 Work Thread와 UI Thread를 지원하지만, 원칙적으로 한 가지의 Thread만 사용할 수 있습니다. 이러한 점으로 Work Thread를 사용 시 연산 처리를 빠르게 할 수 있지만 UI에 접근하지 못하고,
UI Thread를 사용 시 사용자 인터페이스에 접근할 수 있지만, 연산 처리가 느린 불편함이 있습니다.
SmartThread는 각 Thread의 단점을 보완하기 위해 Work Thread와 UI Thread의 장점을 동시에 사용하는 기능이 있습니다. Work Thread로 연산 처리 후 ReportProgress 함수를 호출하면 UI Thread를 우회하여 간접적으로 호출합니다.
이 경우 Work Thread의 장점인 반복적인 연산을 빠르게 처리 후 UI Thread의 장점인 사용자 인터페이스에 직접 접근하여 갱신할 수 있습니다.
2. Work Thread와 UI Thread의 장점만 사용하는 방법
SmartThread를 사용해 연산과 동시에 사용자 인터페이스를 갱신하는 방법은 [CASE-1] UI Thread를 사용하는 방법과 [CASE-2] Work Thread와 UI Thread를 조합하여 사용하는 방법이 있습니다.
[CASE-1]의 경우 UI Thread를 사용해 연산과 사용자 인터페이스 갱신을 동시에 하는 대신 속도가 느린 단점이 있습니다. 하지만 [CASE-2]의 경우 Work Thread를 사용해 빠르고 반복적인 연산 처리를 하는 동시에 UI Thread를 우회하여 호출해 사용자 인터페이스를 갱신할 수 있습니다.
Work Thread의 빠른 연산 처리 속도와 UI Thread의 사용자 인터페이스에 직접 접근할 수 있는 장점만을 사용하기 위해 SmartThread의 인터페이스인 WorkerReportProgress 속성과 ReportProgress 함수를 사용해야 합니다. UI Thread를 간접적으로 호출하기 위해 WorkerReportProgress 속성을 true로 설정 후
Work Thread로 빠르고 반복적인 연산 처리를 하는 동시에 일정한 조건에 의해 사용자 인터페이스를 갱신하기 위해 ReportProgress 함수를 호출하여 UI Thread를 간접적으로 호출하여 처리합니다.
private int m_iCount;
private int[] m_ADCRawData;
private int m_iOldiProgressPercentage;
private void Form1_Load(object sender, EventArgs e)
{
m_ADCRawData = new int[1000000];
m_iOldiProgressPercentage = 0;
smartThread1.Start();
private void smartThread1_UIThreadFunction(SmartX.ThreadArgs args)
{
// UI Thread로 반복적인 연산 처리 시작
// smartADC로부터 데이터를 읽어 m_ADCRawData에 저장
m_ADCRawData[m_iCount++] = smartADC1.ReadData(0);
// 연산 처리 종료
int iProgressPercentage;
iProgressPercentage = (int)(((decimal)m_iCount / (m_ADCRawData.Length)) * 100.0m);
// smartADC를 통해 데이터를 50000개 단위로 받았을 경우 사용자 인터페이스 갱신
if ((((iProgressPercentage % 5) == 0)) && (m_iOldiProgressPercentage != iProgressPercentage))
{
smartProgressBar1.Value = iProgressPercentage;
m_iOldiProgressPercentage = iProgressPercentage;
[CASE-2] Work Thread와 UI Thread를 조합하여 사용한 경우
private int m_iCount;
private int[] m_ADCRawData;
private int m_iOldiProgressPercentage;
private void Form1_Load(object sender, EventArgs e)
{
m_ADCRawData = new int[1000000];
m_iOldiProgressPercentage = 0;
smartThread1.WorkerReportsProgress = true;
private void smartThread1_ThreadFunction(SmartX.ThreadArgs args)
{
// Work Thread로 UI Thread보다 빠르지만 사용자 인터페이스에 직접 접근 불가능
// 따라서 SmartThread1.ReportsProgress() 메서드를 호출하여 간접적으로
// 사용자 인터페이스를 갱신 처리함
// Work Thread로 반복적이면서 빠른 연산 처리 시작
// smartADC로부터 데이터를 읽어 m_ADCRawData에 저장
m_ADCRawData[m_iCount++] = smartADC1.ReadData(0);
// 연산 처리 종료
int iProgressPercentage;
iProgressPercentage = (int)(((decimal)m_iCount / (m_ADCRawData.Length)) * 100.0m)
if ((((iProgressPercentage % 5) == 0)) && (m_iOldiProgressPercentage != iProgressPercentage))
{
smartThread1.ReportProgress(iProgressPercentage);
{
// 사용자 인터페이스 갱신 : SmartProgressBar의 게이지 상승
smartProgressBar1.Value = args.iProgressPercentage;
3. Work Thread와 UI Thread의 조합에 따른 활용 방법
Work Thread는 반복적이고 빠른 연산을 처리할 때 사용하기 적절하기 때문에 SmartADC와 SmartGPIO를 제어할 때 유용하게 사용됩니다. SmartADC와 SmartGPIO를 통해 ADC와 GPIO 값을 읽어오거나, SmartADC 또는 SmartGPIO를 통해 빠르고
반복적으로 값을 읽어 정의된 계산 및 로직 코드에 따라 제어 출력을 빠르게 처리할 수 있습니다. 하지만 Work Thread의 한계점은 사용자 인터페이스(UI)에 직접적으로 접근하지 못하기 때문에 사용하는데 제한이 있습니다. 사용자 인터페이스(UI)를 갱신하기 위해 UI Thread를 사용해 구현한다면
처리할 데이터가 방대하며 빠르고 반복적인 연산 처리가 필요한 경우 권장하지 않습니다.
이러한 한계점을 보완한 Work Thread와 UI Thread의 장점만을 조합하여 사용하는 방법이 있습니다. Work Thread를 통해 반복적인 연산을 빠르게 처리하여 조건에 부합하면 ReportProgress 함수를 호출하여 UI Thread를 간접적으로 호출하고,
사용자 인터페이스(UI)를 갱신합니다. 이 방법을 활용하여 SmartADC를 통해 일정한 수치의 데이터를 읽은 후 사용자 인터페이스에 해당 값을 보여주거나, SmartGPIO를 통해 입력 신호를 감지하여 조건에 부합하는 값을 사용자 인터페이스에 표현하는 등의 처리를 할 수 있습니다.
4. 활용 예제 프로그램 소스 코드
본 예제는 Work Thread를 통해 ADC 값을 읽어 배열에 저장하는 반복적이고 빠른 연산 처리를 하며 Sampling Count 기준 5%(50,000번)마다 UI Thread를 간접적으로 호출하여 SmartProgressBar에 Count 진행도를, SmartDraw에 읽어온 ADC 값의 수치를 갱신합니다.
private const int SAMPLECOUNT = 1000000;
// Thread 이벤트가 발생했을 때 Count를 저장할 변수
private int m_iCount;
// 읽어온 ADC값을 저장할 변수
private int[] m_ADCRawData;
// PutData를 몇번 호출했을 때 Chart에 데이터를 출력할지 설정하는 변수
private int m_iADCDrawChartStep;
// 이번 Percentage값을 저장할 변수
private int m_iOldiProgressPercentage;
// List 템플릿 : 차트에 그릴 데이터 저장
private List〈int〉 m_listChartDatas = new List〈int〉();
private void Form1_Load(object sender, EventArgs e)
{
smartForm1.MainForm = this;
labSampleCnt.Text = "Sampling Count : " + SAMPLECOUNT.ToString();
radBackWorker.Checked = true;
// UI Thread를 우회하여 호출하기 활성화
smartThread1.WorkerReportsProgress = true;
// Chart 설정
smartDraw1.Chart.SetChartConfig();
smartDraw1.BackLayer.ImageDraw(Properties.Resources.SmartDraw, 0, 0);
// Start 버튼을 클릭했을 때 발생하는 이벤트
private void btnStart_Click(object sender, EventArgs e)
{
{
// SamplingCount 값 만큼 배열의 크기를 설정
m_ADCRawData = new int[SAMPLECOUNT];
// SamplingCount 값에서 1000을 나눠 DrawChartStep 값으로 설정
m_iADCDrawChartStep = (SAMPLECOUNT / 1000);
m_iCount = 0;
m_iOldiProgressPercentage = 0;
// 빠른 연산처리가 가능한 Work Thread 이벤트에서 ADC 값을 읽어오고 Data를 저장하는 처리를 진행합니다.
private void smartThread1_ThreadFunction(SmartX.ThreadArgs args)
{
// 따라서 smartThread1.ReportProgress() 함수를 호출해 간접적으로 사용자 인터페이스를 갱신 처리
// work Thread로 반복적이고 빠른 연산 처리 시작
m_ADCRawData[m_iCount++] = smartADC1.ReadData(0);
// Thread 발생횟수와 m_ADCDrawChartStep 값을 나눠 나머지가 0이라면 ADC 값을 연산처리 후 값을 List에 저장
// Ex : SamplingCount = 10,000일 때 m_iADCDrawChartStep의 값은 10이므로 10번 ADC값을 읽었을 때 Data를 저장
if ((m_iCount % m_iADCDrawChartStep) == 0)
{
iCalData = (int)((211.0 / 4095.0) * (float)m_ADCRawData[m_iCount - 1]);
m_listChartDatas.Add(iCalData);
// 연산 처리 종료
int iProgressPercentage;
iProgressPercentage = (int)(((decimal)m_iCount / (m_ADCRawData.Length)) * 100.0m);
// Percentage 값이 5% 단위로 증가되었을 때 UI를 갱신
if ((((iProgressPercentage % 5) == 0)) && (m_iOldiProgressPercentage != iProgressPercentage))
{
smartThread1.ReportProgress(iProgressPercentage);
// 현재값을 이전값으로 저장하여 중복처리 방지
m_iOldiProgressPercentage = iProgressPercentage;
// UI를 갱신할 수 있는 UI Thread 이벤트에서 SmartProgressBar, SmartDraw의 Chart를 갱신
private void smartThread1_UIThreadFunction(SmartX.ThreadArgs args)
{
smartProgressBar1.Value = args.iProgressPercentage;
// List에 추가된 Data들을 모두 Chart의 Data로 입력
foreach (int iADData in m_listChartDatas)
{
// 현재까지 PutData 메소드를 통해 입력된 데이터를 모두 Chart로 출력
smartDraw1.Chart.ChartNowDraw();
// 다음 Data를 저장하기 위한 List 초기화
m_listChartDatas.Clear();
5. 활용 예제 프로그램 사용 방법 및 결과 5-1) 활용 예제 프로그램 사용 방법
① : SmartProgressBar를 통해 Sampling Count(1,000,000)를 기준으로 ADC 값을 읽어온 정도를 표시합니다.
※ SmartProgressBar를 통해 두 가지 방법의 처리 속도를 비교하시기 바랍니다.
② : SmartDraw를 통해 읽어온 ADC 값을 표시합니다.
③ : 사용할 Thread를 선택합니다.
④ : SmartThread의 상태를 설정합니다.
- Start 버튼 : SmartThread를 시작합니다.
- STOP 버튼 : SmartThread를 일시 중지합니다.
- Init 버튼 : SmartThread를 종료합니다.
[IEC1000-Serie 기준] Sampling Count : 1,000,000번 | |
---|---|
[CASE-1] UI(UserInterface) Thread | [CASE-2] Background Worker Thread |
약 5분 38.83초 | 약 58.09초 |
IEC1000-Series 기준으로 Sampling Count를 1,000,000번으로 설정 후 측정했을 때 [CASE-1]의 경우 약 5분 38.83초가, [CASE-2]의 경우 약 58.09초가 소요됐습니다. 데이터가 적고 연산 처리의 속도에 제한이 없으며 UI 갱신이 필요할 경우 [CASE-1] UI Thread만 사용해 구현하는 것을 권장하지만, 데이터가 많고 반복적인 연산 처리가 빨라야 하며 UI 갱신이 필요할 경우 [CASE-2] Work Thread와 UI Thread을 조합하여 사용하는 방법으로 구현하는 것을 권장합니다.