UI Thread에 대해서 알아보자....
(기본적으로 지금부터 하는말은 내가 이해한 수준으로 하는 말이므로 실질적으로는 다를수도 있다...)
WPF의 경우 UI에 데이터를 변경 할 시에는 UI Thread라는 Thread에서 모두 처리가 된다.
즉, 우리는 단순히 Label의 Content를 수정하지만, 내부적으로 파고 들어가면 UI Thread가 해당 작업을 해준다는 것..
그만큼 우리가 신경쓸게 많이 없단 이야기!! 그만큼 편하다!!!!
하지만 이러한 문제 때문에 문제가 발생되는 경우도 존재한다.
WPF의 기본 Thread(MainWindow의 작업)에서는 문제가 발생하지 않지만, 만약 다른 Thread를 만들어서 처리를 한다면.
UI Thread와 새로 만든 Thread간의 UI Component 접근에 문제가 생기는 부분..
이러한 부분을 WPF의 UI Thread 문제라고 한다
이러한 부분은 구글에서 검색만해도..
이렇게나 많이 나온다..
(내 글이 이제 검색하면 나왔으면 좋겠다............)
일단.. UI Thread의 문제나는 코드에 대해서 알아보자..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestProject"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="650">
<Grid >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Label x:Name="lb" Content="ABCDEF"/>
</Grid>
<Grid Grid.Row="1">
<StackPanel Orientation="Horizontal">
<Button Width="120" Content="CHANGE" Click="Button_Click"/>
<Button Width="120" Content="THREAD_CHANGE" Click="Button_Click2"/>
</StackPanel>
</Grid>
</Grid>
</Window>
|
cs |
간단하게 만들었다..
2개의 버튼을 생성하고 상단의 Label을 배치..
Code-Behind로는
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TestProject
{
/// <summary>
/// MainWindow.xaml에 대한 상호 작용 논리
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
lb.Content = "CHANGE";
}
private void Button_Click2(object sender, RoutedEventArgs e)
{
Thread A_START = new Thread(lb_change);
A_START.Start();
}
private void lb_change()
{
lb.Content = "THREAD_CHANGE";
}
}
}
|
cs |
2개의 버튼을 매칭시켜놨고, 1개의 버튼은 자체 STA Thread에서 Label을 변경
또 다른 1개는 새로운 Thread를 생성하여 Label을 변경.
이렇게 처리하였다.
기본적인 STA에서는 값이 변경이 잘 되지만... 새로운 Thread 생성 시 UI Thread 문제가 발생된다.
(아주 잘 작동하는 모습)
그렇다면 이렇게 새로운 Thread 생성 시 문제가 발생시에 어떻게 처리하여야 하는가..?
방법은 새로운 Thread에서 UI를 수정하는것이 아닌 기존 STA Thread에서 UI를 수정하도록 해주는 것..
그 작업이 Dispacher이다.
아래와 같이 코드를 수정해보도록 하자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace TestProject
{
/// <summary>
/// MainWindow.xaml에 대한 상호 작용 논리
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
lb.Content = "CHANGE";
}
private void Button_Click2(object sender, RoutedEventArgs e)
{
Thread A_START = new Thread(lb_change);
A_START.Start();
}
private void lb_change()
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
lb.Content = "THREAD_CHANGE";
}));
}
}
}
|
cs |
Thread 내부에서 Label을 변경해주는 부분을 Dispacher를 통해 처리를 하도록 하였다..
어떻게 처리를 한다면 문제 없이 Label의 데이터가 변경이 된다.
물론 이렇게 Dispacher를 사용해도 되고 아래와 같이 사용해도 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace TestProject
{
/// <summary>
/// MainWindow.xaml에 대한 상호 작용 논리
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
lb.Content = "CHANGE";
}
private void Button_Click2(object sender, RoutedEventArgs e)
{
Thread A_START = new Thread(lb_change);
A_START.Start();
}
private void lb_change()
{
//Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
//{
// lb.Content = "THREAD_CHANGE";
//}));
if (lb.Dispatcher.CheckAccess())
{
lb.Content = "THREAD_CHANGE_2";
}
else
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
lb.Content = "THREAD_CHANGE_2";
}));
}
}
}
}
|
cs |
크게 다른부분은 없고 label의 Dispacher에 접근하여 해당 UI를 수정할 수 있는 Thread인지를 확인 한 후 수정할 수 있는 Thread의 경우 바로 수정, ㅇ니라면 UI Thread에서 처리. 하는 방식..
해당 사항이 한번 체크를 하고 하는 방식이므로 더 좋은 방안이라고 생각되기도 한다.
이상 끝!!
코드는 여기