繁体   English   中英

C#等待任务+无限循环仍冻结UI

[英]C# await tasks + infinite loop still freezing the UI

我正在尝试使用(Tasks)async / await从外部源获取适当的'结构'来监视游戏的状态,以便无限循环地运行任务,但是其当前编写方式似乎只是冻结我的用户界面。

到目前为止,我有:

(在“状态机”类中)

// Start monitoring the game state for changes
public void Start()
{
    tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;
    IsRunning = true;
    task = Task.Factory.StartNew(async () =>
    {
        while (true)
        {
            await Task.Run(()=>CheckForStateChange());
            await Task.Delay(1000); // Pause 1 second before checking state again
        }
    }, token, TaskCreationOptions.LongRunning, TaskScheduler.FromCurrentSynchronizationContext());
}

没有上面的“ Task.Delay”行,UI将完全冻结。 使用“ Task.Delay”行不会冻结,但是如果我尝试拖动窗口,它将跳回到我开始拖动它的位置。

我对当前代码的假设是执行“ await Task.Run()”,并在完成时执行“ await Task.Delay()”,然后在完成时返回while(true)无限循环的开始。 (即不并行运行)。

CheckForStateChange()签名如下:

private void CheckForStateChange()
{
    // ... A bunch of code to determine and update the current state value of the object
}

没什么特别的,简单的非异步方法。 我在此处阅读了有关StackOverflow的大量示例/问题,并且我曾经让CheckForStateChange返回一个Task(方法中包含等待的动作)和许多其他代码迭代(具有相同的结果)。

最后,我从主要的win32窗体(按钮)调用Start()方法,如下所示:

private void btnStartSW_Click(object sender, EventArgs e)
{
    // Start the subscription of the event handler
    if(!state.IsRunning)
    {
        state.StateChange += new SummonersWar.StateChangeHandler(OnGameStateChange);
        state.Start();
    }
}

我认为以上代码是到目前为止我编写代码结构的最简单形式,但是显然它仍然不是“正确”编写的。 任何帮助,将不胜感激。

更新:发布方(状态机类):

    // ------ Publisher of the event ---
    public delegate void StateChangeHandler(string stateText);
    public event StateChangeHandler StateChange;
    protected void OnStateChange() // TODO pass text?
    {
        if (StateChange != null)
            StateChange(StateText());
    }

其中StateText()方法只是检索当前状态的“文本”表示形式的一种临时方法(在此刻它实际上是一个占位符,直到我将其组织为整齐的结构)

IsRunning纯粹是一个公共布尔。

UI线程中的处理程序:

private void OnGameStateChange(string stateText)
{
    // Game State Changed (update the status bar)
    labelGameState.Text = "State: " + stateText;
}

用户界面为何冻结

在主要问题方面:您已经通过Task.Run调用了CheckForStateChange ,因此除非您的CheckForStateChange包含被编组回UI线程的调用(即Control.InvokeSynchronizationContext.Post/Send否则您将无法冻结UI。 SynchronizationContext.Post/Send可以显式使用,也可以通过在UI TaskScheduler上启动的Task隐式使用。

开始寻找的最佳位置是StateChange处理程序(即StateChangeHandler )。 还可以看看StateChange事件在何处引发。 您可以在这些站点之一中找到线程编组代码。

其他事宜

您正在将指向UI SynchronizationContextTaskScheduler传递给外部任务。 您还传递了TaskCreationOptions.LongRunning 简单来说,您是在告诉任务工厂“在专用线程和当前线程上启动任务”。 这两个是互斥的要求,您可以放心地将它们都删除。

由于上述原因,如果您的外部任务恰好在UI线程上执行,则不会真正使您绊倒,因为内部调用包含在Task.Run ,但这可能不是您期望的行为。

您将Task.Factory.StartNew的结果存储在task字段或属性中。 但是请注意,您的Task.Factory.StartNew调用返回Task<Task> ,因此,除非您对其调用Unwrap并进入内部任务,否则保存的Task实例将几乎立即转换为完成状态。 为了避免整个混乱,只需使用Task.Run创建外部任务(因为它内置了Unwrap语义)。 如果这样做,您可以Task.Run内部Task.Run完全运行,如下所示:

public bool IsRunning
{
    get
    {
        return task.Status == TaskStatus.Running;
    }
}

public void Start()
{
    tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;

    task = Task.Run(async () =>
    {
        while (true)
        {
            CheckForStateChange(token);

            token.ThrowIfCancellationRequested();

            await Task.Delay(1000); // Pause 1 second before checking state again
        }
    }, token);

    // Uncomment this and step through `CheckForStateChange`.
    // When the execution hangs, you'll know what's causing the
    // postbacks to the UI thread and *may* be able to take it out.
    // task.Wait();
}

由于您具有CancellationToken ,因此需要将其传递给CheckForStateChange ,并定期对其进行检查-否则,仅在启动Task时对其进行一次检查,然后再也不会进行检查。

请注意,我还提供了一个不同的IsRunning实现。 不稳定的状态很难纠正。 如果该框架免费提供给您,则应使用它。

最后的话

总体而言,整个解决方案似乎有些a脚,应该以更被动的方式完成某些工作-但我可以想到这种设计有效的方案。 我只是不相信您的人确实是其中之一。

编辑:如何查找阻止用户界面的内容

我将对此表示down惜,但这里有:

确定导致UI线程回发的原因的肯定方法是对其进行死锁。 SO上有很多线程告诉您如何避免这种情况,但是就您而言-我们会故意使它发生,并且您将确切地知道在轮询更改时需要避免的调用-尽管是否有可能避免这些呼吁,还有待观察。

我已经放置了一个task.Wait指令,在我的代码片段的末尾。 前提是您在UI线程上调用Start ,这将导致CheckForStateChange 某些内容导致死锁,并且您将知道需要解决的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM