我有一个 Tile
对象的多维数组。
我的程序首先选择一个随机位置(例如(0,0((,访问随机邻居Tile
,将其标记为访问并移至下一个邻居直到没有可用的未访问的邻居。p>这是我的代码:
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
->移动到邻居 ->将其标记为当前瓷砖 ->绘制当前瓷砖 -> ...
您的BeginInvoke
正在调度(排队(某些特定Dispatcher
要完成的"工作",即通常是您的主UI消息循环/处理线程。
当您在" while循环"中时,您已经在Dispatcher
线程上处理/进行工作(但这是针对不同的/以前的消息(。
(通常(Dispatcher
无法从队列中处理下一个"调度"消息,直到您完成了所做的工作并控制返回到Dispatcher
...然后Dispatcher
可以选择下一条消息下一步(这可能是您的,或者可能是某人,具体取决于优先级(。
(有关Windows消息循环/调度程序的工作方式的更深入的描述,请参阅此处:使用dispatcher.invoke使我的线程安全吗?(
这一点代码将从Task
开始,该CC_14将继续访问随机图块,直到您的所有HasUnvisitedNeighbours()
返回false。
您可以使用.Invoke
(同步版(而不是.BeginInvoke
和.Wait
组合。
注意:通常可以说是使用.BeginInvoke
,而不是Tile
0,而不是在某些情况下.Invoke
可以陷入僵局。...但是我认为在这种情况下,您可以使用。
您可以执行HasUnvisitedNeighbours();
,然后可以进行.Draw
,如果您喜欢的话,可以在单个" Invoke"中选择下一个瓷砖。
但是,我可以将其分为2个"调用",因为您可能可以重组它,以便您仅获得一次未访问的瓷砖的列表(通过查询所有瓷砖。再次所有的瓷砖 - 那是因为您是参观瓷砖的人...所以您知道它们是否已经访问过。
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
);
}
}
因此,调度员似乎等待整个循环完成,然后绘制最后一个瓷砖对象。
是的,Dispatcher.BeginInvoke
计划最终在调度程序线程上执行的委托。但是,为什么您要启动一项新任务以调用Dispatcher.BeginInvoke
?这没什么意义。
您可以使用同步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;
}
});
}