簡體   English   中英

WPF / XAML:如何執行線程處理並防止主UI忙/死?

[英]WPF / XAML: How do I execute threaded processes and prevent the main UI from being busy / freezing?

我有一個XAML應用程序,可以用作自動化的UI。 整個自動化過程可能需要20到30個小時才能完全執行,因此我創建了一個Task類對象,該對象實際上包裝了Thread方法(Start / Stop / Reset)。

但是,當我在Task對象下運行自動化方法時,XAML UI繁忙,並且我無法與其他控件進行交互,包括用於切換Thread.Set()標志的“暫停”按鈕。

還有另一篇文章, 在沒有其他線程的情況下防止UI凍結

當有人在MSDN文章中推薦的地方推薦BackgroundWorker類時,如果它在UI中操作對象時使用此方法是一個壞主意,我的這樣做是為了顯示狀態計數: http : //msdn.microsoft.com/zh-cn /library/system.componentmodel.backgroundworker.aspx

有什么想法嗎?

    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));
    }
}

在MSDN文章中有人建議使用BackgroundWorker類的地方,如果它在UI中操作對象,則使用此方法是一個壞主意,我的這樣做是為了顯示狀態計數

實際上, BackgroundWorker對此非常理想,因為它是針對此類情況而設計的。 警告是您不應在DoWork內部更改UI元素,而應通過ReportProgressProgressChanged事件。

該警告存在的原因是在后台線程上執行了“ DoWork”。 如果從此處設置UI元素值,則會得到交叉線程異常。 但是,ReportProgress / ProgressChanged會自動將調用封送回給您合適的SynchronizationContext

看一下WPF中的Dispatcher對象。 您可以並且應該在您的方案中在后台線程上運行長時間運行的任務,而BackgroundWorker是執行此任務的好方法。 當您需要更新UI時,需要驗證對UI線程的訪問,如果沒有,請使用分派器在UI線程上調用更新方法。

這里有兩個可能的原因:首先,阻塞任務正在阻塞UI線程,而不是在后台線程上運行;其次,后台線程正在使UI線程處於飢餓狀態,因此它永遠沒有機會響應輸入。 您需要找出其中的哪種情況。 一種簡單的方法是,在您的Click處理程序中,使用Debug.WriteLine當前線程ID(Thread.CurrentThread.ManagedThreadId),然后在RunFullAutomation回調中執行相同的操作。

如果這些打印相同的數字,那么您將遇到第一個問題。 Reed和TheZenker已為此提供了解決方案。

如果這些命令打印出不同的數字,則說明您已經在工作線程中,並且還有第二個問題。 (BackgroundWorker可能會讓您更優雅地進入工作線程,並有助於更新UI,但不會停止飢餓。)在這種情況下,最簡單的解決方法可能是設置_thread.Priority = ThreadPriority.BelowNormal; 在啟動工作線程之前。

順便說一句,您的代碼似乎永遠不會真正調用AutomationThread.Start ,這意味着甚至沒有執行RunFullAutomation回調。 這只是錯字嗎?

我建議不要發布自己的Task類,因為.NET 4完全支持使用Task Parallel Library在后台異步運行任務。也就是說,您可以執行Reed的建議並使用理想的BackgroundWorker或希望更好地控制任務執行方式的性質,您可以使用System.Threading.Tasks中的Task類並實現類似以下內容:

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();
    }       
}

DoWork()您使用WPF SynchronizationContext並發布消息以更新所需的UI wiget。

該示例具有進度條和標簽控件,該標簽控件在for循環的每次迭代中都會更新。使用CancellationTokenSource支持CancellationTokenSource ,該CancellationTokenSource在每次迭代中都會進行檢查。

希望這可以幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM