2014-10-21 54 views
0

這個問題有人在這裏的許多線程問,但我找不到明確的答案我的情況,我有一個簡單的代碼,Button,TextboxButtonClickEventHandler,儘管後者儘管爲Async,保持UI阻塞,代碼:用戶界面被異步/等待屏蔽

public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private async void ButtonBase_OnClick(object sender, RoutedEventArgs e) 
     { 
      Task t = new Task(() => 
      { 
       for (int i = 0; i < 10000; i++) 
       { 
        Dispatcher.Invoke((()=> 
        { 
         TextBox.Text += i.ToString(); 
        })); 
       } 
      }); 
      t.Start(); 
      await t; 

      //Something else 
     } 

    } 

的XAML:

<Window x:Class="WpfApplication1.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Button Margin="180,10,185,277" x:Name="Button" Click="ButtonBase_OnClick"> 
      Ok 
     </Button> 
     <TextBox Margin="29,83,40,33" x:Name="TextBox"></TextBox> 
    </Grid> 
</Window> 

首先,爲什麼會出現這種情況? 其次,我應該做些什麼來使UI保持正常工作而不會掛起,始終遵循這個邏輯。

回答

5

正如其他人所指出的,如果您嘗試頻繁更新UI,最終會阻止用戶交互。

就你的代碼而言,你應該使用Task.Run而不是任務構造函數,而使用IProgress<T>而不是Dispatcher.Invoke。我有一個IProgress<T> implementation that handles throttling for you,它可以作爲這樣的:

private async void ButtonBase_OnClick(object sender, RoutedEventArgs e) 
{ 
    using (var progress = ObservableProgress<int>.CreateForUi(value => UpdateUi(value))) 
    { 
    await Task.Run(() => BackgroundOperation(progress)); 
    } 

    ... 
} 

private static void UpdateUi(int progressUpdate) 
{ 
    TextBox.Text += progressUpdate.ToString(); 
} 

private static void BackgroundOperation(IProgress<int> progress) 
{ 
    for (int i = 0; i < 10000; i++) 
    { 
    if (progress != null) 
     progress.Update(i); 
    } 
} 

沒有人爲的拖延必要的。

+1

恭喜100k! – 2014-10-22 03:38:49

+0

@YuvalItzchakov:謝謝! – 2014-10-22 12:01:25

5

定義「封鎖」。您發佈的代碼將而不是阻止用戶界面。但它會確保它真的很忙,這可能會產生似乎,因爲它被阻止。就此而言,即使它不是真的,也可以將其用於所有實際目的。

你基本上是在你的GUI線程上執行拒絕服務攻擊。你在一個緊密的循環中有一個輔助線程,它只是在GUI線程上執行一些東西。不要那樣做,事情會好很多。

+0

通過阻止我的意思是沒有響應,掛起,不能點擊,無法移動窗口或與UI交互......請不要使我的事情複雜我不想做這個操作,我想繼續執行它,同時保持用戶界面的響應 – AymenDaoudi 2014-10-21 23:14:11

+1

@AymenDaoudi雖然他是對的......該代碼將鎖定CPU。在你的'for'循環中添加一個'Thread.Sleep(20)'(在這個例子中是20毫秒,意思是20毫秒),它會開始正常工作。 – Ruslan 2014-10-21 23:19:59

+0

@AymenDaoudi:對不起,如果你覺得我複雜的事情。我正在做的是回答你的問題。 「塊」一詞有不同的含義,因此我最初的陳述。從技術上講,你的GUI線程不會被阻塞,但是根據你自己對「無響應」的定義,它絕對是,正是因爲我說的原因。正如魯斯蘭所指出的那樣,如果你在這個循環中加入任何顯着的延遲,情況會好得多。當然,人們希望實際上你的輔助線程有更多有趣的事情要做,甚至不需要爲了保持GUI的工作而被任意放慢速度。 :) – 2014-10-21 23:25:55