저는 멀티 스레드 UI를 작성하고 있습니다. 긴 프로세스를 BackgroundWorker 클래스에서 처리하고 UI에 작은 타이머를 사용하여 프로세스의 소요 시간을 추적하고 싶습니다. 그러한 UI를 구축 한 것은 이번이 처음 이었기 때문에 웹상의 관련 자료를 읽었습니다. 내 테스트 코드는 이렇게이다 :WPF 및 멀티 스레딩 질문
private BackgroundWorker worker;
private Stopwatch swatch = new Stopwatch();
private delegate void simpleDelegate();
System.Timers.Timer timer = new System.Timers.Timer(1000);
string lblHelpPrevText = "";
private void btnStart_Click(object sender, RoutedEventArgs e)
{
try
{
worker = new BackgroundWorker(); //Create new background worker thread
worker.DoWork += new DoWorkEventHandler(BG_test1);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BG_test1end);
worker.RunWorkerAsync();
simpleDelegate del = new simpleDelegate(clockTicker);
AsyncCallback callBack = new AsyncCallback(clockEnd);
IAsyncResult ar = del.BeginInvoke(callBack, null);
lblHelpText.Text = "Processing...";
}
finally
{
worker.Dispose(); //clear resources
}
}
private void clockTicker()
{
//Grab Text
simpleDelegate delLblHelpText = delegate()
{ lblHelpPrevText = this.lblHelpText.Text; };
this.Dispatcher.BeginInvoke(DispatcherPriority.Send, delLblHelpText);
//Start clock
timer.Elapsed += new ElapsedEventHandler(clockTick);
timer.Enabled = true;
swatch.Start();
}
private void clockTick(object sender, ElapsedEventArgs e)
{
simpleDelegate delUpdateHelpTxt = delegate()
{ this.lblHelpText.Text = String.Format("({0:00}:{1:00}) {2}", swatch.Elapsed.Minutes, swatch.Elapsed.Seconds, lblHelpPrevText); };
this.Dispatcher.BeginInvoke(DispatcherPriority.Send, delUpdateHelpTxt);
}
private void BG_test1(object sender, DoWorkEventArgs e)
{
//this.lblHelpText.Text = "Processing for 10 seconds...";
Thread.Sleep(15000);
}
private void BG_test1end(object sender, RunWorkerCompletedEventArgs e)
{
this.lblHelpText.Text = "Process done.";
this.timer.Enabled = false;
this.swatch.Stop();
this.swatch.Reset();
}
static void clockEnd(IAsyncResult ar)
{
simpleDelegate X = (simpleDelegate)((AsyncResult)ar).AsyncDelegate;
X.EndInvoke(ar);
}
아이디어는 버튼을 클릭 할 때, 우리는 다음 초마다 그것에 시간을 추가 ("... 처리"예를 들어) 레이블에서 상태 텍스트를 가지고있다. Timer 클래스의 UI 요소는 다른 스레드에 있으므로 UI 요소에 액세스 할 수 없으므로 대리자를 사용하여 텍스트를 가져와 설정해야했습니다.
작동하지만 더 좋은 방법이 있습니까? 코드는 이러한 기본적인 작업을하는 것처럼 보입니다. 하단의 EndInvoke 비트도 완전히 이해하지 못했습니다. 이 스레드에서 코드 스 니펫을 얻었습니다. Should One Always Call EndInvoke a Delegate inside AsyncCallback?
저는 BeginInvoke의 결과를 EndInvoke라는 아이디어로 이해합니다. 그러나이 상황에서 이것을 사용하는 올바른 방법입니까? 리소스 누출에 대해 걱정할 뿐이지 만 디버깅 할 때 타이머가 작동하기 전에 콜백이 실행되는 것처럼 보입니다.
감사합니다,하지만 난 수동으로 진행 상황을 업데이트해야하지 않을까요? 이 경우에는 작업의 실행 시간을 결정할 수 없습니다 (SQL 데이터베이스에서 수천 행의 데이터를 가져올 계획입니다). 그래서 타이머가 작업 실행 시간을 추적하기 만하면됩니다. – MHTri
@MHTri : 실행 시간과 진행 상황은 서로 다릅니다. 미리 잡을 행이 몇 개 있는지 알고 있으면 진행 상황을 업데이트 할 수 있습니다 ('SELECT COUNT'); "얼마나 오랫동안 작업이 실행되고 있는지"에 대해서는 실제로 타이머가 필요합니다. 그러나 타이머와 'BackgroundWorker'사이에는 * 커플 링이 없어야합니다. – Jon
흠. 별도의 SQL 유틸리티 클래스에서 별도의 메서드로 SQLDataAdapter.Fill을 사용하고 있습니다. 따라서 진행률을 Fill 블록으로 추정하지 않습니다 (전역이 아닌 한 DataTable.Rows.Count 속성에 액세스 할 수 없음을 의미 함). 변수는 완료 될 때까지 RowChangedEvent를 발생시키지 않습니다)? – MHTri