[英]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.