簡體   English   中英

在不阻塞 UI 線程的情況下運行后台任務並等待其結果

[英]Running a background task and waiting for its result without blocking the UI thread

我想在我的 WinForms 應用程序上創建一個啟動畫面。 我創建啟動畫面表單並運行初始化操作。

public async Task RunApplication()
{
    splash = new SplashWindow();
    splash.Show();
    
    await Task.Run(InitializeAsync);
    
    Application.Run(new frmMain());
}

//the InitializeAsync function
private async Task InitializeAsync()
{
   splash.Status = "Test";
}

//the status property
public string Status
{
    get { return status; }
    set {
        if (status != value)
        {
            status = value;
            lblStatus.InvokeIfRequired(() => lblStatus.Text = value);
        }
    }
}

public static void InvokeIfRequired(this ISynchronizeInvoke snc,
                                         MethodInvoker action)
{
    if (snc.InvokeRequired) {
        snc.Invoke(action, null);
    } else {
        action();
    }
}

如您所見,我希望能夠將 label 更改為當前狀態。 但是,當我運行此代碼時,只要我點擊snc.Invoke(action, null); 行,程序掛起。 我做了一些研究/調試,似乎當我執行Task.Run時,它會阻塞 UI 線程,並且由於在該線程上創建了啟動畫面,因此程序永遠無法在 UI 線程上運行該操作。

我最終做的是等到任務完成並不斷地做應用程序事件。

var task = Task.Run(InitializeAsync);

while(!task.IsCompleted)
{
    Application.DoEvents();
    Thread.Sleep(10);
}

Applicaiton.Run(new frmMain());

這行得通。 但是,我想知道是否有更優雅的解決方案。

首先,擺脫InvokeIfRequired InvokeRequired不管它有多常見——是一種嚴重的代碼異味。

相反,您可以擺脫Task.Run並直接調用InitializeAsync

public async Task RunApplication()
{
  splash = new SplashWindow();
  splash.Show();

  await InitializeAsync();

  Applicaiton.Run(new frmMain());
}

private async Task InitializeAsync()
{
  splash.Status = "Test";
}

public string Status
{
  get => lblStatus.Text;
  set => lblStatus.Text = value;
}

如果您出於某種原因確實需要使用Task.Run (即,如果InitializeAsync有 CPU-bound 或阻塞工作要做),那么您可以使用Progress<T>

public async Task RunApplication()
{
  splash = new SplashWindow();
  splash.Show();

  var progress = new Progress<string>(update => splash.Status = update);
  await Task.Run(() => InitializeAsync(progress));

  Applicaiton.Run(new frmMain());
}

private async Task InitializeAsync(IProgress<string>? progress)
{
  progress?.Report("Test");
}

public string Status
{
  get => lblStatus.Text;
  set => lblStatus.Text = value;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM