我有一个Tile
个对象的多维数组。
我的程序首先在随机位置选择Tile
(例如(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];
的最后一个值。所以似乎调度程序等待整个循环完成,然后它绘制最后一个{{ 1}}对象。
我的目的是绘制当前Tile
- &gt;移动到邻居 - &gt;将其标记为当前图块 - &gt;绘制当前图块 - &gt; ...
答案 0 :(得分:3)
您的BeginInvoke
正在安排(排队)由特定Dispatcher
完成的某些“工作”,即通常是您的主要UI消息循环/处理线程。
当您处于“while循环”时,您已经在Dispatcher
线程上处理/工作(但它是针对不同/之前的消息)。
(通常)Dispatcher
无法处理队列中的下一个“dispatch”消息,直到你完成了你正在做的事情并且控制权返回Dispatcher
...然后Dispatcher
可以选择接下来要处理的消息(可能是您的消息,也可能是某些消息,具体取决于优先级)。
(有关Windows消息循环/调度程序如何工作的更深入描述,请参阅:Does using dispatcher.Invoke make my thread safe?)
这段代码将从Task
开始,这将导致访问随机磁贴,直到您的所有HasUnvisitedNeighbours()
都返回false。
您也可以使用.Invoke
(同步版本)代替.BeginInvoke
和.Wait
组合。
注意:通常说使用.BeginInvoke
而不是.Invoke
更可取,因为在某些情况下.Invoke
可能会死锁....但我认为在这种情况下您可以使用任
如果您愿意,可以执行HasUnvisitedNeighbours();
,然后选择.Draw
,然后在一个“调用”中选择下一个图块。
然而,我把它分成2个“调用”,因为你可以重构它,这样你只得到一次未访问的瓦片列表(通过查询所有的瓦片.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
);
}
}
答案 1 :(得分:0)
所以似乎调度员等待整个循环完成,然后它绘制最后的Tile对象。
是的,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;
}
});
}