다른 클래스에서 실행 중인 다른 스레드에서 UI를 업데이트하는 방법
저는 현재 C#에 대한 첫 번째 프로그램을 작성하고 있으며 언어에 대해 매우 생소합니다(지금까지 C와만 작업했습니다).저는 많은 조사를 해봤지만, 모든 답이 너무 일반적이어서 그것을 제대로 할 수 없었습니다.
여기 제 (매우 일반적인) 문제가 있습니다.저는 사용자가 작성한 몇 개의 텍스트 상자에서 입력을 가져온 다음 이를 사용하여 많은 계산을 수행하는 WPF 애플리케이션을 가지고 있습니다.2-3분 정도 소요될 것으로 예상되므로 진행 상황 표시줄과 현재 상태를 알려주는 텍스트 블록을 업데이트하고 싶습니다.또한 사용자의 UI 입력을 저장하여 스레드에 제공해야 하므로 개체를 만들 때 사용하는 세 번째 클래스가 있으며 이 개체를 백그라운드 스레드로 전달하고 싶습니다.분명히 저는 다른 스레드에서 계산을 실행하여 UI가 동결되지 않도록 하겠지만, 모든 계산 방법이 다른 클래스의 일부이기 때문에 UI를 업데이트하는 방법을 모르겠습니다.많은 연구 끝에 제가 생각하기에 가장 좋은 방법은 백그라운드 작업자가 아닌 파견자와 TPL을 사용하는 것입니다. 하지만 솔직히 그들이 어떻게 작동하는지 확신할 수 없습니다. 그리고 약 20시간의 시행착오 끝에 다른 답변을 통해 제가 직접 질문을 하기로 결정했습니다.
여기 제 프로그램의 매우 간단한 구조가 있습니다.
public partial class MainWindow : Window
{
public MainWindow()
{
Initialize Component();
}
private void startCalc(object sender, RoutedEventArgs e)
{
inputValues input = new inputValues();
calcClass calculations = new calcClass();
try
{
input.pota = Convert.ToDouble(aVar.Text);
input.potb = Convert.ToDouble(bVar.Text);
input.potc = Convert.ToDouble(cVar.Text);
input.potd = Convert.ToDouble(dVar.Text);
input.potf = Convert.ToDouble(fVar.Text);
input.potA = Convert.ToDouble(AVar.Text);
input.potB = Convert.ToDouble(BVar.Text);
input.initStart = Convert.ToDouble(initStart.Text);
input.initEnd = Convert.ToDouble(initEnd.Text);
input.inita = Convert.ToDouble(inita.Text);
input.initb = Convert.ToDouble(initb.Text);
input.initc = Convert.ToDouble(initb.Text);
}
catch
{
MessageBox.Show("Some input values are not of the expected Type.", "Wrong Input", MessageBoxButton.OK, MessageBoxImage.Error);
}
Thread calcthread = new Thread(new ParameterizedThreadStart(calculations.testMethod);
calcthread.Start(input);
}
public class inputValues
{
public double pota, potb, potc, potd, potf, potA, potB;
public double initStart, initEnd, inita, initb, initc;
}
public class calcClass
{
public void testmethod(inputValues input)
{
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
int i;
//the input object will be used somehow, but that doesn't matter for my problem
for (i = 0; i < 1000; i++)
{
Thread.Sleep(10);
}
}
}
테스트 방법 내부에서 UI를 업데이트하는 방법에 대해 간단한 설명을 해주시면 감사하겠습니다.저는 C#과 객체 지향 프로그래밍에 익숙하지 않기 때문에, 너무 복잡한 답변은 이해하지 못할 가능성이 매우 높기 때문에 최선을 다하겠습니다.
또한 누군가 일반적으로 더 나은 아이디어를 가지고 있다면 (아마도 배경 작업자나 다른 것을 사용할 것입니다) 저는 그것을 볼 수 있습니다.
먼사용야합다니를 사용해야 .Dispatcher.Invoke다른 스레드에서 UI를 변경하고 다른 클래스에서 UI를 변경하려면 이벤트를 사용할 수 있습니다.
을 보내고 때 . UI런다메로에그등변 UI알리려고던다니집이때를할트벤계클디서래에스스산패치하고을항사경음록하고당벤트에인이해래서클스에▁when▁then에▁event▁the(로▁you▁you▁throw▁register던▁event▁and▁can▁you▁the다▁the▁to▁main▁class▁that▁dispatch▁to:▁inculation▁class▁the니▁the▁cal집▁changes이때▁and를▁the트할벤▁uis그▁ui▁to)▁notify▁ui▁want서▁in클계고
class MainWindow : Window
{
private void startCalc()
{
//your code
CalcClass calc = new CalcClass();
calc.ProgressUpdate += (s, e) => {
Dispatcher.Invoke((Action)delegate() { /* update UI */ });
};
Thread calcthread = new Thread(new ParameterizedThreadStart(calc.testMethod));
calcthread.Start(input);
}
}
class CalcClass
{
public event EventHandler ProgressUpdate;
public void testMethod(object input)
{
//part 1
if(ProgressUpdate != null)
ProgressUpdate(this, new YourEventArgs(status));
//part 2
}
}
이것은 여전히 자주 방문하는 질문과 답변인 것처럼 보이기 때문에, 저는 이 답변을 제가 지금 어떻게 할 것인지와 함께 업데이트하고 싶습니다.NET 4.5) - 몇 가지 다른 가능성을 보여드릴 것이기 때문에 이는 조금 더 깁니다.
class MainWindow : Window
{
Task calcTask = null;
void buttonStartCalc_Clicked(object sender, EventArgs e) { StartCalc(); } // #1
async void buttonDoCalc_Clicked(object sender, EventArgs e) // #2
{
await CalcAsync(); // #2
}
void StartCalc()
{
var calc = PrepareCalc();
calcTask = Task.Run(() => calc.TestMethod(input)); // #3
}
Task CalcAsync()
{
var calc = PrepareCalc();
return Task.Run(() => calc.TestMethod(input)); // #4
}
CalcClass PrepareCalc()
{
//your code
var calc = new CalcClass();
calc.ProgressUpdate += (s, e) => Dispatcher.Invoke((Action)delegate()
{
// update UI
});
return calc;
}
}
class CalcClass
{
public event EventHandler<EventArgs<YourStatus>> ProgressUpdate; // #5
public TestMethod(InputValues input)
{
//part 1
ProgressUpdate.Raise(this, status); // #6 - status is of type YourStatus
// alternative version to the extension for C# 6+:
ProgressUpdate?.Invoke(this, new EventArgs<YourStatus>(status));
//part 2
}
}
static class EventExtensions
{
public static void Raise<T>(this EventHandler<EventArgs<T>> theEvent,
object sender, T args)
{
if (theEvent != null)
theEvent(sender, new EventArgs<T>(args));
}
}
@1) "동기식" 계산을 시작하고 백그라운드에서 실행하는 방법
" it으로 시작하는 방법:@2) "wait it": 가 돌아오기 전에 되지만, 여기서 계산은 메소드가 반환되기 전에 완료됩니다.async/awaitUI가 차단되지 않음(BTW: 이벤트 핸들러가 반환해야 하므로 이러한 이벤트 핸들러는 의 유일한 유효한 용도임 - 다른 모든 경우에 사용)
새 @3) 새 @3) .Thread이제 사용합니다.Task 합니다.calcTask회의원 하지만, 더 쉽게 할 수 있고 몇 또한 백그라운드에서 새 스레드를 시작하고 작업을 실행하지만 처리하기가 훨씬 쉬우며 다른 이점도 있습니다.
@4) 여기서도 작업을 시작하지만, 이번에는 작업을 반환하므로 "비동기화 이벤트 처리기"가 "대기"할 수 있습니다.우리는 또한 만들 수 있습니다.async Task CalcAsync()그리고 나서.await Task.Run(() => calc.TestMethod(input)).ConfigureAwait(false); (으)는: 더입니다.ConfigureAwait(false)교착 상태를 피하기 위해, 만약 당신이 사용한다면 당신은 이것에 대해 읽어봐야 합니다.async/await하는 것이 처럼) 흐름을 , 서설명는것것때많문이기에있수동만지초여일를할래기우워로크플한을하이무너▁the▁as,▁as,있▁which만지▁but수▁in▁workflow동▁here)▁same,Task.Run는 유일한 "대기 작업"이며 작업을 반환하고 컨텍스트 전환 하나만 저장하면 실행 시간을 절약할 수 있는 마지막 작업입니다.
@5) 이제 "status object"를 쉽게 주고받을 수 있도록 "status object"를 사용합니다.
@6) 여기서 저는 아래에 정의된 확장을 사용합니다. 이 확장은 (사용 편의성과는 별도로) 이전 예제에서 가능한 인종 조건을 해결합니다.거기서 사건이 일어날 수도 있었습니다.null에 if-체크를 선택합니다. 하지만 바로 그 순간에 이벤트 핸들러가 다른 스레드에서 제거된 경우에는 호출 전에 선택합니다. 확이이복을핸상 "여얻고동" 안에 등록되어 있기 에 여기서 이런 일이 발생할 수 없습니다Raise방법.
제가 여기서 커브를 던져드릴게요.만약 내가 그것을 한 번 말했다면, 나는 그것을 백 번 말했습니다.다음과 같은 마샬링 작업Invoke또는BeginInvoke작업자 스레드 진행률로 UI를 업데이트하는 가장 좋은 방법은 아닙니다.
이 경우 일반적으로 작업자 스레드가 진행률 정보를 공유 데이터 구조에 게시하도록 하고 UI 스레드는 정기적으로 폴링합니다.이것은 몇 가지 장점이 있습니다.
- .
Invoke과중한 - UI 컨트롤이 업데이트될 때 UI 스레드가 지시합니다...당신이 정말로 그것에 대해 생각할 때 어쨌든 그것이 되어야 하는 방식.
- . 큐를 오버런할 위험은 없습니다.
BeginInvoke작업자 스레드에서 사용되었습니다. - 스레드의 가 없습니다. UI 입니다.
Invoke. - UI 및 작업자 스레드 모두에서 더 많은 처리량을 얻을 수 있습니다.
Invoke그리고.BeginInvoke비용이 많이 드는 작업입니다.
그래서 당신의calcClass진행률 정보를 저장할 데이터 구조를 만듭니다.
public class calcClass
{
private double percentComplete = 0;
public double PercentComplete
{
get
{
// Do a thread-safe read here.
return Interlocked.CompareExchange(ref percentComplete, 0, 0);
}
}
public testMethod(object input)
{
int count = 1000;
for (int i = 0; i < count; i++)
{
Thread.Sleep(10);
double newvalue = ((double)i + 1) / (double)count;
Interlocked.Exchange(ref percentComplete, newvalue);
}
}
}
당신의 럼그당의에서.MainWindow는 용적법을 합니다.DispatcherTimer진행률 정보를 주기적으로 폴링합니다.성을 합니다.DispatcherTimer을 높이기 위해Tick상황에 가장 적합한 간격으로 이벤트가 발생시킵니다.
public partial class MainWindow : Window
{
public void YourDispatcherTimer_Tick(object sender, EventArgs args)
{
YourProgressBar.Value = calculation.PercentComplete;
}
}
당신이 그것을 사용해야 한다는 당신의 말이 맞습니다.DispatcherUI 스레드에 대한 컨트롤을 업데이트하고 UI 스레드에서 장시간 실행되는 프로세스를 실행하면 안 됩니다.장시간 실행 프로세스를 UI 스레드에서 비동기식으로 실행하더라도 성능 문제가 발생할 수 있습니다.
주의해야 할 것은Dispatcher.CurrentDispatcherUI 스레드가 아닌 현재 스레드에 대한 디스패처를 반환합니다.사용할 수 있을 것 같습니다.Application.Current.Dispatcher사용 가능한 경우 UI 스레드의 디스패처에 대한 참조를 가져오려면 UI 디스패처를 백그라운드 스레드에 전달해야 합니다.
일반적으로 스레드 작업을 위해 태스크 병렬 라이브러리를 사용합니다.BackgroundWorker저는 그냥 사용하기가 더 쉽다고 생각합니다.
예를들면,
Task.Factory.StartNew(() =>
SomeObject.RunLongProcess(someDataObject));
어디에
void RunLongProcess(SomeViewModel someDataObject)
{
for (int i = 0; i <= 1000; i++)
{
Thread.Sleep(10);
// Update every 10 executions
if (i % 10 == 0)
{
// Send message to UI thread
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action)(() => someDataObject.ProgressValue = (i / 1000)));
}
}
}
UI와 상호 작용하는 모든 항목은 UI 스레드에서 호출해야 합니다(고정 개체가 아닌 경우).이를 위해 발송인을 사용할 수 있습니다.
var disp = /* Get the UI dispatcher, each WPF object has a dispatcher which you can query*/
disp.BeginInvoke(DispatcherPriority.Normal,
(Action)(() => /*Do your UI Stuff here*/));
는 Begin 는 Begin 사용다니합을을 사용합니다.여기서 호출합니다. 일반적으로 백그라운드 작업자는 UI가 업데이트될 때까지 기다릴 필요가 없습니다.,▁use▁if니▁▁can를 사용하시면 됩니다.Invoke은 비긴을 이라고 부르지 않도록 조심해야 합니다.빠른 속도로 자주 호출하면, 이것은 정말 끔찍해질 수 있습니다.
그런데, BackgroundWorker 클래스는 이런 종류의 탁을 도와줍니다.이를 통해 보고 변경사항(%)을 허용하고 백그라운드 스레드에서 UI 스레드로 자동으로 변경사항을 전송합니다.대부분의 스레드 <> 업데이트 UI 작업에서 BackgroundWorker는 훌륭한 도구입니다.
만약 이것이 계산이 길다면 저는 백그라운드 작업자로 갈 것입니다.진행률 지원이 있습니다.취소도 지원합니다.
http://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx
여기 콘텐츠에 바인딩된 텍스트 상자가 있습니다.
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.Write("backgroundWorker_RunWorkerCompleted");
if (e.Cancelled)
{
contents = "Cancelled get contents.";
NotifyPropertyChanged("Contents");
}
else if (e.Error != null)
{
contents = "An Error Occured in get contents";
NotifyPropertyChanged("Contents");
}
else
{
contents = (string)e.Result;
if (contentTabSelectd) NotifyPropertyChanged("Contents");
}
}
의 주 스레드로 할 입니다.UI thread하기 위하여updateUI 는 UI를 발생시킬 뿐입니다.exceptions사방으로 던져질 것입니다.
따라서 WPF에 있기 때문에 보다 구체적으로beginInvoke이에 대하여dispatcher이렇게 하면 UI 스레드에서 수행해야 하는 작업(일반적으로 UI 업데이트)을 실행할 수 있습니다.
은 또한 "도 있습니다.UI의 신의에business으로써 컨트롤/폼의 컨폼/에를참유지사를 할 수 .dispatcher.
감사합니다, 마이크로소프트가 WPF에서 그것을 알아냈습니다 :)
든Control 표시줄,폼 진표시, 단추, 양식등다가 .Dispatcher위에. 됩니다. 당신은 그것을 줄 수 있습니다.Dispatcher하나의Action 수야하며해다, 올스자호서로니출동합으에레드른행바an((an))에서됩니다.Action는 기능 대리인과 같습니다.
여기서 예를 찾을 수 있습니다.
물론, 다른 클래스에서 액세스할 수 있는 컨트롤이 있어야 합니다. 예를 들어, 다음과 같습니다.public그리고 언급을 건네는 것.Window다른 클래스로 이동하거나 진행률 표시줄에만 참조를 전달할 수 있습니다.
보다 더 답을 할 .BackgroundWorker나를 도와주는 것 같았고, 지금까지 그것을 다루는 대답은 비참할 정도로 불완전했습니다. 페이지를 입니다.MainWindow다음과 같은 이미지 태그가 있습니다.
<Image Name="imgNtwkInd" Source="Images/network_on.jpg" Width="50" />
BackgroundWorker네트워크에 연결되어 있는지 여부를 표시하는 프로세스:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
public partial class MainWindow : Window
{
private BackgroundWorker bw = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
// Set up background worker to allow progress reporting and cancellation
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
// This is your main work process that records progress
bw.DoWork += new DoWorkEventHandler(SomeClass.DoWork);
// This will update your page based on that progress
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
// This starts your background worker and "DoWork()"
bw.RunWorkerAsync();
// When this page closes, this will run and cancel your background worker
this.Closing += new CancelEventHandler(Page_Unload);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BitmapImage bImg = new BitmapImage();
bool connected = false;
string response = e.ProgressPercentage.ToString(); // will either be 1 or 0 for true/false -- this is the result recorded in DoWork()
if (response == "1")
connected = true;
// Do something with the result we got
if (!connected)
{
bImg.BeginInit();
bImg.UriSource = new Uri("Images/network_off.jpg", UriKind.Relative);
bImg.EndInit();
imgNtwkInd.Source = bImg;
}
else
{
bImg.BeginInit();
bImg.UriSource = new Uri("Images/network_on.jpg", UriKind.Relative);
bImg.EndInit();
imgNtwkInd.Source = bImg;
}
}
private void Page_Unload(object sender, CancelEventArgs e)
{
bw.CancelAsync(); // stops the background worker when unloading the page
}
}
public class SomeClass
{
public static bool connected = false;
public void DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
int i = 0;
do
{
connected = CheckConn(); // do some task and get the result
if (bw.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
Thread.Sleep(1000);
// Record your result here
if (connected)
bw.ReportProgress(1);
else
bw.ReportProgress(0);
}
}
while (i == 0);
}
private static bool CheckConn()
{
bool conn = false;
Ping png = new Ping();
string host = "SomeComputerNameHere";
try
{
PingReply pngReply = png.Send(host);
if (pngReply.Status == IPStatus.Success)
conn = true;
}
catch (PingException ex)
{
// write exception to log
}
return conn;
}
}
자세한 내용은 https://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx 에서 확인하시기 바랍니다.
언급URL : https://stackoverflow.com/questions/9602567/how-to-update-ui-from-another-thread-running-in-another-class
'sourcecode' 카테고리의 다른 글
| 목표-C 정적 클래스 수준 변수 (0) | 2023.04.28 |
|---|---|
| VBA에 3개의 연산자가 있습니까? (0) | 2023.04.28 |
| 이클립스와 같은 Android Studio 바로 가기 (0) | 2023.04.28 |
| 텍스트 블록 WPF에 하이퍼링크 추가 (0) | 2023.04.23 |
| SQLite3 데이터베이스에서 열 이름 목록을 가져오려면 어떻게 해야 합니까? (0) | 2023.04.23 |