簡體   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