2011-02-12 1 views
0

자동화를위한 UI 역할을하는 XAML 응용 프로그램이 있습니다. 전체 자동화는 20-30 시간에서 완전히 실행될 수 있으므로 기본적으로 스레드 메소드 (시작/중지/재설정)를 래핑하는 Task 클래스 객체를 만들었습니다.WPF/XAML : 스레드 된 프로세스를 실행하고 주 UI가 사용 중이거나 고정되지 않게하려면 어떻게해야합니까?

그러나 Task 개체 아래에서 자동화 메서드를 실행하면 XAML UI가 사용 중이므로 Thread.Set() 플래그를 토글하는 Pause 단추를 비롯한 다른 컨트롤과 상호 작용할 수 없습니다.

사람이 MSDN 기사는 내 상태의 수를 표시하기위한 목적으로 수행하는 UI에서 오브젝트를 조작하는 경우 때 사용하는 것은 좋지 않다 가지 설명 BackgroundWorker 구성 클래스를 권장 또 다른 포스트 Prevent UI from freezing without additional threads

있다 : http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

이 주변의 아이디어? 누군가가이 MSDN의 문서를 가지 설명 BackgroundWorker 구성 클래스를 추천

private void OnButtonStartAutomationClick(object sender, RoutedEventArgs e) 
    { 
     btnPauseAutomation.IsEnabled = true; 
     Automation.Task AutomationThread = new Automation.Task(RunFullAutomation); 
    } 

    private void RunFullAutomation() 
    { 
     // do stuff that can take 20+ hours 
     // threaded so I can utilize a pause button (block) 
    } 

class Task 
{ 
    private ManualResetEvent _shutdownFlag = new ManualResetEvent(false); 
    private ManualResetEvent _pauseFlag = new ManualResetEvent(true); 
    private Thread _thread; 
    private readonly Action _action; 

    public Task(Action action) 
    { 
     _action = action; 
    } 

    public void Start() 
    { 
     ThreadStart ts = new ThreadStart(DoDelegatedMethod); 
     _thread = new Thread(ts);    
     _thread.Start(); 
     _thread.Priority = ThreadPriority.Lowest; 
    } 

    public void Resume() 
    { 
     _pauseFlag.Set(); 
    } 

    public void Stop() 
    { 
     _shutdownFlag.Set(); 
     _pauseFlag.Set(); 
     _thread.Join(); 
    } 

    private void DoDelegatedMethod() 
    { 
     do 
     { 
      _action(); 
     } 
     while (!_shutdownFlag.WaitOne(0)); 
    } 
} 
+0

메모 - 프레임 워크의 Task 클래스와 충돌하기 때문에'Task'라는 클래스를 만들지 말 것을 권장합니다. http://msdn.microsoft.com/en-us/library/system.threading. tasks.task.aspx –

답변

3

나쁜 것이 나의 상태를 표시하기위한 목적으로 수행하는 UI,의 개체를 조작하는 경우 때를 사용하는 아이디어

를 계산이다

BackgroundWorker은 실제로이 유형의 시나리오에 맞게 설계 되었기 때문에 실제로 이상적입니다. 경고 메시지는 DoWork 내부의 UI 요소를 변경하지 말고 ReportProgressProgressChanged 이벤트를 통해 변경해야합니다.

경고가있는 이유는 "DoWork"가 백그라운드 스레드에서 실행되기 때문입니다. 거기에서 UI 요소 값을 설정하면 크로스 스레딩 예외가 발생합니다. 그러나 ReportProgress/ProgressChanged는 자동으로 다시 SynchronizationContext에 대한 호출을 마샬링합니다.

1

WPF에서 Dispatcher 개체를 살펴보십시오. 시나리오에서 장기 실행 작업을 백그라운드 스레드에서 실행할 수 있으며 BackgroundWorker는이를 수행하는 좋은 방법입니다. UI를 업데이트해야 할 경우 UI 스레드에 대한 액세스를 확인하고 필요하지 않은 경우 디스패처를 사용하여 UI 스레드에서 업데이트 메서드를 호출해야합니다.

1

여기에는 두 가지 원인이 있습니다. 첫째, 차단 작업이 백그라운드 스레드에서 실행되는 것이 아니라 UI 스레드를 차단하고 두 번째로 백그라운드 스레드가 UI 스레드를 굶어 죽을 수 없도록하는 것입니다. 입력에 응답한다. 이 중 어느 것을 찾을 필요가 있습니다. 이 작업을 수행하는 일반적인 방법은 Click 처리기에서 Debug.WriteLine에 현재 스레드 ID (Thread.CurrentThread.ManagedThreadId)를 지정하고 RunFullAutomation 콜백에서 동일한 작업을 수행하는 것입니다.

동일한 번호가 인쇄되면 첫 번째 문제가 발생합니다. Reed와 The Zenker는 이에 대한 해결책을 제시했습니다.

다른 숫자가 인쇄되면 이미 작업자 스레드에 있고 두 번째 문제점이 있습니다. BackgroundWorker는 작업자 스레드를보다 우아하게 만들 수 있으며 UI 업데이트에 도움이되지만 기아를 막을 수는 없습니다.이 경우 가장 간단한 수정은 작업자 스레드를 시작하기 전에 _thread.Priority = ThreadPriority.BelowNormal;으로 설정하는 것입니다.

실제로 코드는 실제로 AutomationThread.Start을 호출하는 것으로 보이지 않습니다. 즉, RunFullAutomation 콜백이 실행되지 않습니다. 이것은 단지 오타입니까?

+0

그래, 오타입니다. 그 점을 지적 해 주셔서 감사합니다! – Wibble

1

.NET 4는 작업 병렬 라이브러리를 사용하여 백그라운드에서 비동기 적으로 작업을 실행하는 데 완벽한 지원을 제공한다는 점에서 자신의 Task 클래스를 배포하지 말 것을 권합니다. 그렇다면 Reed가 제안한 것을 수행하고 적합 또는 작업시는 실행 방법의 본질에보다 효율적으로 관리를 선호하는 경우, 당신은 너무 같은 System.Threading.Tasks에서 작업 클래스를 사용하여 구현할 수 있습니다 : 당신이 WPF SynchronizationContext에 사용 DoWork()에서

public partial class MainWindow : Window 
{ 
    CancellationTokenSource source = new CancellationTokenSource(); 
    SynchronizationContext context = SynchronizationContext.Current; 
    Task task; 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void DoWork() 
    { 
     for (int i = 0; i <= 100; i++) 
     { 
      Thread.Sleep(500); //simulate long running task 
      if (source.IsCancellationRequested) 
      { 
       context.Send((_) => labelPrg.Content = "Cancelled!!!", null); 
       break; 
      } 
      context.Send((_) => labelPrg.Content = prg.Value = prg.Value + 1, null); 
     } 
    } 

    private void Start_Click(object sender, RoutedEventArgs e) 
    { 
     task = Task.Factory.StartNew(DoWork, source.Token); 
    } 

    private void Cancel_Click(object sender, RoutedEventArgs e) 
    { 
     source.Cancel(); 
    }  
} 

을 필요한 UI 위젯을 업데이트하기위한 메시지를 게시하십시오.

이 예제에는 진행률 표시 줄과 for 루프의 각 반복마다 업데이트되는 레이블 컨트롤이 있습니다. 각 반복에서 확인되는 CancellationTokenSource을 사용하여 취소가 지원됩니다.

희망이 도움이됩니다.

관련 문제