2017-12-01 13 views
0

以下のWPFコードにはボタンとラベルしかありません。私はこのような振る舞いを期待していました。私はボタンが無効になっているのを見て、 "Doing Stuff"がラベルに書かれていて、THEN UIがフリーズします。しかし、そうではありません。私は "Doing Stuff"ラベルのUIが最初まで凍結されているのを見ていません。この背後にある論理を教えてもらえますか?なぜThread.SleepはUIを即座にフリーズさせるのですか?Thread.Sleepの背後にあるロジック

using System.Threading.Tasks; 
using System.Threading; 
using System.Windows; 

namespace MessagePump 
{ 
/// <summary> 
/// Interaction logic for MainWindow.xaml 
/// </summary> 
public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void btnDoStuff_Click(object sender, RoutedEventArgs e) 
    { 
     btnDoStuff.IsEnabled = false; 
     lblStatus.Content = "Doing Stuff"; 
     Thread.Sleep(4000); 
     lblStatus.Content = "Not doing anything"; 
     btnDoStuff.IsEnabled = true; 
    } 
} 
} 
; 
+0

あなたが代わりにディスパッチャを使用する必要があります。this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal、非同期()=> {Task.Delay(4000)を待つ; <あなたの残りの部分コード>}); –

+0

'Thread.Sleep(4000)'を 'await Task.Delay(TimeSpan.FromSeconds(4));'に置き換え、UIスレッドがブロックされないようにイベントハンドラ 'async'を作ります。 –

答えて

5

Thread.Sleepは、現在のスレッド(この場合はUIスレッド)をブロックします。つまり、他のタスクは実行できません。 これで、長いブロッキング操作が完了するまで、UIスレッドは「画面のリフレッシュ」のようなメッセージを実行できなくなりました。

WindowsにUIを再描画する必要があるが、スレッドがスリープ操作によってブロックされているため、そのメッセージを実行できないことを示すWM_PAINTのような保留中のメッセージがあります。 あなただけのThread.sleep前に、次のコード行を追加する場合は、UIは、UIスレッドをブロックし、アプリケーションを呼び出すことに注意してくださいを行い、凍結

Application.DoEvents(); 

である前に、ラベルが更新されますがわかります。 DoEventsは、ほとんどの場合、良い解決策ではありません。特定の問題を解決するには、UIスレッドをブロックしDoEventsを呼び出すよりも、より良い方法があります。 (たとえば、バックグラウンドスレッドまたはタスクを使用して長期実行操作を実行し、イベントを使用してUIにそのタスクの進行状況を通知するなど)。

長時間実行されているタスクは、アプリケーションが応答しなくなるためUIスレッドで実行されるべきではありません。したがって、別のスレッドに作業をオフロードする方が良いです。 .NET 4以降、好ましい方法はTAP(タスクベースの非同期パターン)を使用することです。

あなたの質問では、あなたが実行したいいくつかのCPU集約的なアルゴリズムを模擬するためにThread.Sleepを使用していると推測します。

await Task.Run (() => SomeLongRunningMethod()) 
+1

私はそれを理解しています。しかし、thread.sleep行を実行する前に、 "Doing Stuff"というラベルの内容を変更します。スクリーン上でその変化を見ることができないのはなぜですか?その行はThread.Sleep行の上にあります...それはとても速いのでしょうか? – Lyrk

+1

_ "ラベルの内容を" Doing Stuff "に変更しましたが、画面上でその変更が見られないのはなぜですか?"というのはUIスレッドがその変更を見るためにコントロールを再描画する必要があるからです。しかし、それはあなたがそれを眠らせたので、できません。チームAがゴールを決めてデータが増えるが、アリーナのスコアボードを更新する人が眠っているようだ... – Fildor

+0

lolありがとう。 UIにはその変更を反映する時間さえありません.... – Lyrk

5

Thread.SleepブロックUIスレッド: あなたは次のように新しいタスクを開始することでこれを行うことができます。クリックハンドラasyncを宣言し、代わりにTask.Delayを呼び出す:

private async void btnDoStuff_Click(object sender, RoutedEventArgs e) 
{ 
    btnDoStuff.IsEnabled = false; 
    lblStatus.Content = "Doing Stuff"; 

    await Task.Delay(4000); 

    lblStatus.Content = "Not doing anything"; 
    btnDoStuff.IsEnabled = true; 
} 
関連する問題