2017-04-26 1 views
1

私はTileの多次元配列を持っています。Dispatcherは配列から最後のオブジェクトのみを描画します

私のプログラムは、ランダムな位置(例:(0,0))のランダムなアドレスTileを訪問し、訪れたとマークし、利用可能な未訪問のネイバーがなくなるまで次のネイバーに移動します。

これは私のコードです:

while (HasUnvisitedNeighbours()) 
{ 
    Task.Run(() => 
    { 
     Application.Current.Dispatcher.BeginInvoke(new Action(() => 
     { 
      CurrentTile.Draw(); 
     })); 
     Thread.Sleep(500); 
    }); 

    //1. Choose randomly a neighbour 
    List<char> Keys = new List<char>(CurrentTile.Neighbours.Keys); 
    char randomKey = Keys[rnd.Next(Keys.Count)]; 

    CurrentTile = CurrentTile.Neighbours[randomKey]; 
    CurrentTile.HasBeenVisited = true; 
} 

私は私のプログラムを通じてデバッグきた、と私はいることが確認されました:

CurrentTile = CurrentTile.Neighbours[randomKey]; 

が正しく正しい隣人に各ループを変更し、ループすることをその隣に未訪問の隣人がいないときに停止する。

しかし中:

Task.Run(() => 
{ 
    Application.Current.Dispatcher.BeginInvoke(new Action(() => 
    { 
     CurrentTile.Draw(); 
    })); 
    Thread.Sleep(500); 
}); 

CurrentTile.Draw()は常に(デバッガで、CurrentTile = CurrentTile.Neighbours[randomKey];の最後の値を同じ値を持っているように見える、全体のループのディスパッチャ待機が終了しているようだし、それを描くようにします。最後Tileオブジェクト

私の意図は、現在のTile描画することです - 。あなた> ...

答えて

3

- >現在のタイルを描く - >隣人に移動 - 現在のタイルのように>マークをは、特定のDispatcher(通常はメインのUIメッセージループ/ハンドリングスレッド)によって行われるべきいくつかの「仕事」をスケジューリング(キューイング)しています。

あなたが「whileループ」にいるときには、すでにDispatcherスレッドで作業をしています(ただし、別の/以前のメッセージ用です)。あなたはDispatcherにリターンをやって制御されているものを終了しました...そしてDispatcherがプロセスにどのメッセージを選択することができるまで

(通常)Dispatcherは、キューから次の「派遣」のメッセージを処理することはできません次に(あなたのものかもしれないし、優先順位に応じて誰かがelsesかもしれない)。

(Windowsメッセージループ/ディスパッチャがどのように機能するかのより徹底的な説明については、この参照:Does using dispatcher.Invoke make my thread safe?)を

コードのこのビットは、すべてあなたまで訪問するランダムタイルを引き起こし続けるTaskをオフに開始しますHasUnvisitedNeighbours()はfalseを返します。

代わりに.BeginInvoke.Waitの組み合わせの代わりに.Invoke(同期バージョン)を使用することもできます。

注:それは多くの場合、.Invokeデッドロックすることができ、特定の状況のように.BeginInvokeはなく、.Invokeを使用することが好ましいと言われています....が、私はあなたがいずれかを使用でき、この場合だと思います。

HasUnvisitedNeighbours();を入力して.Drawを入力し、好きな場合はただ1つの「呼び出し」で次のタイルを選択することができます。

しかし、私は2つの "呼び出し"を分割して、おそらく1回だけ(タイルのすべてのクエリ.HasBeenVisitedプロパティを使用して)未訪問のタイルのリストを取得できるようにしました。すべてのタイルをもう一度尋ねる - それはあなたがタイルを訪問している人だからです。今訪問したかどうかは分かります。

void VisitAllNeighbours() 
{ 
    Task.Run(() => 
    { 
     while (true) 
     { 
      bool bHasUnvisitedNeighbours = false; 

      DispatcherOperation dispopunvisitedtiles = Application.Current.Dispatcher.BeginInvoke(new Action(() => 
      { 
       bHasUnvisitedNeighbours = HasUnvisitedNeighbours(); 
      }; 

      dispopunvisitedtiles.Wait(); 

      if (!bHasUnvisitedNeighbours) 
       break; 

      DispatcherOperation dispopvisitnext = Application.Current.Dispatcher.BeginInvoke(new Action(() => 
      { 
       CurrentTile.Draw(); 

       //1. Choose randomly a neighbour 
       List<char> Keys = new List<char>(CurrentTile.Neighbours.Keys); 
       randomKey = Keys[rnd.Next(Keys.Count)]; 

       CurrentTile = CurrentTile.Neighbours[randomKey]; 
       CurrentTile.HasBeenVisited = true; 

      })); 

      // Take out this "wait" if you want multiple "visits" to be 
      // pending/scheduled instead of only one at a time - be 
      // careful that your "random visiting" selection code 
      // doesn't cause too many "visits" to be outstanding though! 
      // 
      // Note: each visit never occurs at the same time as another 
      // one as the "Dispatcher"/message loop is providing the 
      // serialization. 

      dispopvisitnext.Wait(); 

      Thread.Sleep(500); // if you want a "delay" between each Tile visit 
     ); 
    } 
} 
+0

ああ、私は最初に言うのではなく、最後の 'Tile'をキューに入れるのはなぜでしょうか?また、私が取った道を描きたいのであれば、これは間違った実装ですか?私もdispatcherTimerを試しましたが、それは最初のダニで図面を作った。 – BaronSensei

+0

http://stackoverflow.com/questions/1526678/does-using-dispatcher-invoke-make-my-thread-safe .... Windowsのmessageloopディスパッチャの動作方法の説明です。 –

+0

最後のタイルのみですCurrentTileはループから出てきたときに最後のタイルに設定されているため、描画されます。ループから出て、CurrentTile.Draw()が呼び出されるときだけです。現在のタイルはその時点で最後のタイルを含みます.....あなたの第1、第2、第3などではありません。あなたの "beginvoke"は待ち行列に入れられているだけで、次の「ディスパッチャ」が許可される/それを処理する周りにいる時間。 –

0

だから、全体のループのディスパッチャ待機が終了しているようだし、それは最後のタイルオブジェクトを描画します。

はい、Dispatcher.BeginInvokeは、最終的にディスパッチャスレッドで実行されるようにスケジュールします。しかし、なぜあなたはDispatcher.BeginInvokeに電話するだけで新しい仕事を始めていますか?これはあまり意味がありません。

あなたは方法がディスパッチャスレッド上excecutedされるまで待つように、同期Invokeメソッドを使用することができます。

while (HasUnvisitedNeighbours()) 
{ 
    Application.Current.Dispatcher.Invoke(new Action(() => CurrentTile.Draw())); 
    Thread.Sleep(500); 

    List<char> Keys = new List<char>(CurrentTile.Neighbours.Keys); 
    char randomKey = Keys[rnd.Next(Keys.Count)]; 

    CurrentTile = CurrentTile.Neighbours[randomKey]; 
    CurrentTile.HasBeenVisited = true; 
} 

編集:

UIスレッドが両方あなたのループを実行し、更新することはできません同時にUI。

private void Method() 
{ 
    Task.Run(async() => 
    { 
     while (HasUnvisitedNeighbours()) 
     { 
      await Application.Current.Dispatcher.BeginInvoke(new Action(() => CurrentTile.Draw())); 
      await Task.Delay(500); 

      List<char> Keys = new List<char>(CurrentTile.Neighbours.Keys); 
      char randomKey = Keys[rnd.Next(Keys.Count)]; 

      CurrentTile = CurrentTile.Neighbours[randomKey]; 
      CurrentTile.HasBeenVisited = true; 
     } 
    }); 
} 
+0

nice、私は最後の 'Tile'の代わりに完全なパスを参照してください!,私の唯一の問題は、完全な描画が完了するまで、invoke()呼び出しがUIスレッドをブロックするように見えることです。タイルは一度に一つずつ描かれていますか? – BaronSensei

+0

はい、ブロックされます。そういうわけで、それは機能します。 – mm8

+0

CurrentTile.Draw()は1つのタイルのみを描画しませんか?もちろん、この間にUIスレッドはブロックされます。 – mm8

関連する問題