C# Dispatcher仅从数组中提取最后一个对象

C# Dispatcher仅从数组中提取最后一个对象,c#,wpf,multidimensional-array,C#,Wpf,Multidimensional Array,我有一个多维的Tile对象数组 我的程序首先在随机位置(例如(0,0))选择一个磁贴,访问一个随机的邻居磁贴,将其标记为已访问并移动到下一个邻居,直到没有未访问的邻居可用 这是我的代码: while (HasUnvisitedNeighbours()) { Task.Run(() => { Application.Current.Dispatcher.BeginInvoke(new Action(() => {

我有一个多维的
Tile
对象数组

我的程序首先在随机位置(例如(0,0))选择一个
磁贴
,访问一个随机的邻居
磁贴
,将其标记为已访问并移动到下一个邻居,直到没有未访问的邻居可用

这是我的代码:

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;
}
正确地将每个循环更改为正确的邻居,并且当其旁边没有未访问的邻居时,循环将停止

但在:

Task.Run(() =>
{
    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
    {
        CurrentTile.Draw();
    }));
    Thread.Sleep(500);
});
CurrentTile.Draw()
在调试器中似乎总是具有相同的值(
CurrentTile=CurrentTile.Neights[randomKey];
。因此,调度程序似乎等待整个循环完成,然后绘制最后一个
Tile
对象


我的意图是绘制当前的
磁贴
->移动到邻居->将其标记为当前磁贴->绘制当前磁贴->。

您的
BeginInvoke
正在安排(排队)某些“工作”由特定的
调度器来完成,即通常是您的主UI消息循环/处理线程

当您处于“while循环”中时,您已经在
Dispatcher
线程上处理/执行工作(但它是针对不同的/以前的消息)

(通常情况下,
调度程序
无法处理队列中的下一条“调度”消息,直到您完成所做的操作并控制返回到
调度程序
…然后
调度程序
可以选择下一条要处理的消息(可能是您的消息,也可能是其他人的消息,取决于优先级)

(有关windows消息循环/调度程序工作方式的更深入的描述,请参阅:)

这段代码将启动一个
任务
,该任务将持续导致随机分幅被访问,直到所有
hasunvisitedneights()
返回false

您也可以使用
.Invoke
(同步版本),而不是
.BeginInvoke
.Wait
组合

注意:人们常说最好使用
.BeginInvoke
,而不是
。在某些情况下调用
。调用
可能会死锁……但我认为在这种情况下,您可以使用其中一种

您可以执行
hasunvisitednights();
,然后执行
.Draw
,如果愿意,只需一次“调用”即可选择下一个磁贴

但是,我将其拆分为2个“调用”,因为您可以重新构造它,以便只获得一次未访问的tile列表(通过查询所有tiles.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
        );
    }
}
void visitallneights()
{
Task.Run(()=>
{
while(true)
{
bool BhasunVisitedNeights=假;
DispatcherOperation DispatpopunVisitedTiles=Application.Current.Dispatcher.BeginInvoke(新操作(()=>
{
bhasunVisitedNeights=hasunVisitedNeights();
};
dispopunvisitedtiles.Wait();
如果(!BhasunVisitedNeights)
打破
DispatcherOperation DispatchepVisitNext=Application.Current.Dispatcher.BeginInvoke(新操作(()=>
{
CurrentTile.Draw();
//1.随机选择一个邻居
列表键=新列表(CurrentTile.neights.Keys);
随机键=键[rnd.Next(键数)];
CurrentTile=CurrentTile.Neights[randomKey];
CurrentTile.HasBeenVisited=true;
}));
//如果你想进行多次“访问”,请去掉这个“等待”
//挂起/计划,而不是一次仅一个-可能
//小心你的“随机访问”选择代码
//但不会导致太多的“访问”变得杰出!
//
//注意:每次访问不会与另一次同时进行
//一个是“Dispatcher”/消息循环提供
//序列化。
dispopvisitnext.Wait();
Thread.Sleep(500);//如果您想在每次访问之间获得“延迟”
);
}
}
因此,调度器似乎等待整个循环完成,然后绘制最后一个平铺对象

是的,
Dispatcher.BeginInvoke
计划最终在Dispatcher线程上执行委托。但是为什么启动新任务只是为了调用
Dispatcher.BeginInvoke
?这没有多大意义

您可以使用synchronous
Invoke
方法等待,直到在dispatcher线程上执行该方法:

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;
}
while(hasunVisitedNeights())
{
Application.Current.Dispatcher.Invoke(新操作(()=>CurrentTile.Draw());
睡眠(500);
列表键=新列表(CurrentTile.neights.Keys);
char randomKey=Keys[rnd.Next(Keys.Count)];
CurrentTile=CurrentTile.Neights[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;
        }
    });
}
private void方法()
{
Task.Run(异步()=>
{
while(hasunVisitedNeights())
{
等待Application.Current.Dispatcher.BeginInvoke(新操作(()=>CurrentTile.Draw());
等待任务。延迟(500);
列表键=新列表(CurrentTile.neights.Keys);
char randomKey=Keys[rnd.Next(Keys.Count)];
CurrentTile=CurrentTile.Neights[randomKey];
CurrentTile.HasBeenVisited=true;
}
});
}

啊,我明白了,但是为什么它只将最后一个
磁贴排成队列,而不说第一个。还有,这是错误的吗
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;
        }
    });
}