簡體   English   中英

正確使用 Await & Async

[英]Using Await & Async Properly

我不完全確定我在這里做什么是正確的,因為我沒有經常使用 async/await 方法,並且打算在一個小應用程序中了解更多信息。

代碼:

        public async Task ImportURLs() {

            // read contents of the chosen combobox platform ...
            var comp = File.ReadAllText(@"Platforms\" + comboBoxPlatform.Text).Split('|');
            var reg = comp[0];
            var fl1 = comp[1];
            var fl2 = comp[2];

            string line;
            OpenFileDialog ofd = new OpenFileDialog
            {
                Filter = "URLs.txt|*.txt"
            };
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                if (ofd.FileName.Trim() != string.Empty)
                {
                    using (StreamReader r = new StreamReader(ofd.FileName))
                    {
                        while ((line = r.ReadLine()) != null)
                        {
                            // check fl1 exists in the url first ...
                            var check_1 = Helpers.FindNeedleInHtml(line, fl1);

                            // if it does the root the url and check the reg page ...
                            if (check_1 == "Yes")
                            {
                                var check_2 = Helpers.FindNeedleInHtml(line, fl2);
                                // check_ & check_2 is "Yes" or "No"
                                AddToListView(Helpers.GetRootUrl(line) + reg, check_1, check_2);
                            }

                        }
                    }
                }
            }
        }

        private async void BtnImportURLs_Click(object sender, EventArgs e)
        {
            await Task.Run(() => ImportURLs());
        }

我所做的只是點擊一個按鈕並導入一個 URL 列表,檢查 HTML 中的一個字符串,然后報告是或否。

目標是在不鎖定 UI 的情況下運行應用程序,我可以使用后台工作程序,但是如果我按原樣運行代碼,我會收到錯誤消息:

跨線程操作無效:控制“comboBoxPlatform”從創建它的線程以外的線程訪問。

我可以通過調用繞過它,我在正確的軌道上嗎?

任何幫助,將不勝感激。

正如您所說,您需要從 UI 線程填充組合框。 任何從另一個線程訪問它的嘗試都會給你CrossThreadException 我發現這樣做的最簡單方法是從任務中返回信息,如下所示:

    private async Task<List<string>> GetInformationAsync()
    {
        var returnList = new List<string>();
        Stopwatch sw = new Stopwatch();

        // The UI thread will free up at this point, no "real" work has
        // started so it won;t have hung
        await Task.Run(() =>
        {
            for (var i = 0; i < 10; i++)
            {
                returnList.Add($"Item# {i}");

                // Simulate 10 seconds of CPU load on a worker thread
                sw.Restart();
                while (sw.Elapsed < TimeSpan.FromSeconds(2))
                    ; /* WARNING 100% CPU for this worker thread for 2 seconds */
            }
        });

        // Task that was running on the Worker Thread has completed
        // we return the List<string>

        return returnList;
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        // Get some information and put this into the listBox

        var t = await GetInformationAsync();

        // The CPU intensive task has completed we now have a list of items
        // This will run on the UI thread, as evidenced by no Cross Thread exception
        foreach (string s in t)
            listBox1.Items.Add(s);

    }

並且因為能夠捕獲異常很重要,所以您知道正在運行的獨立任務是否失敗以及失敗的原因。

與上面相同的代碼,但有一些簡單的異常處理。

    private async Task<List<string>> GetInformationAsync()
    {
        var returnList = new List<string>();
        Stopwatch sw = new Stopwatch();

        // The UI thread will free up at this point, no "real" work has
        // started so it won;t have hung
        await Task.Run(() =>
        {

            for (var i = 0; i < 10; i++)
            {
                returnList.Add($"Item# {i}");

                // Simulate 10 seconds of CPU load on a worker thread
                sw.Restart();
                while (sw.Elapsed < TimeSpan.FromSeconds(2))
                    ; /* WARNING 100% CPU for this worker thread for 2 seconds */
            }

            // Lets pretend that something went wrong up above..
            throw new ArgumentNullException("Lets use this exception");

        });

        // Task that was running on the Worker Thread has completed
        // we return the List<string>

        return returnList;
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        // What if something went wrong we want to catch the exception...
        // the previous verion doesn;t let us do that...

        try
        {
            var t = await GetInformationAsync();

            // No exception was thrown
            foreach (string s in t)
                listBox1.Items.Add(s);
        }
        catch
        {
            listBox1.Items.Clear();
            listBox1.Items.Add("Something went wrong!");
        }
    }

您可能希望能夠做的另一件事是向用戶提供進度反饋。 為此,您提到了 Invoke - 顯然這是舊的做法。 許多地方的建議似乎是使用 IProgress。

以下是一些簡單的更改,隨着 CPU 綁定任務的進行,這些更改將近乎實時的結果反饋給用戶。

    private async Task<List<string>> GetInformationAsync(IProgress<int> progress)
    {
        var returnList = new List<string>();
        Stopwatch sw = new Stopwatch();

        // The UI thread will free up at this point, no "real" work has
        // started so it won;t have hung
        await Task.Run(() =>
        {

            for (var i = 0; i < 10; i++)
            {
                // Simulate 10 seconds of CPU load on a worker thread
                sw.Restart();
                while (sw.Elapsed < TimeSpan.FromSeconds(2))
                    ; /* WARNING 100% CPU for this worker thread for 2 seconds */

                returnList.Add($"Item# {i}");

                // Report back to the UI thread
                // increases the progress bar...
                progress.Report((i+1)*10);
            }
        });

        // Task that was running on the Worker Thread has completed
        // we return the List<string>

        return returnList;
    }

    private async void button1_Click(object sender, EventArgs e)
    {

        button1.Enabled = false;

        try
        {
            var progress = new Progress<int>(i => progressBar1.Value = i);

            var t = await GetInformationAsync(progress);

            // No exeception was thrown
            foreach (string s in t)
                listBox1.Items.Add(s);
        }
        catch
        {
            listBox1.Items.Clear();
            listBox1.Items.Add("Something went wrong!");
        }
        finally
        {
            button1.Enabled = true;
        }

    }

正如錯誤所述,您創建的新線程無法訪問ComboBox因為它未在該新線程中實例化。 不過,您對 async await 的想法是正確的。

我認為(這只是一種方法)你最好的辦法是傳入File.ReadAllText(@"Platforms\\" + comboBoxPlatform.Text).Split('|'); 作為參數,以便不需要在新線程中訪問ComboBox

private async void BtnImportURLs_Click(object sender, EventArgs e)
{
    string input = @"Platforms\" + comboBoxPlatform.Text).Split('|');
    await Task.Run(() => ImportURLs(input));
}

暫無
暫無

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

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