调度器仅从数组中绘制最后一个对象



我有一个 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,而不是Tile0,而不是在某些情况下.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;
        }
    });
}

最新更新