我不太确定自己在这里做的是正确的,因为我没有使用太多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'从创建该线程的线程之外的其他线程访问。
我可以通过调用哪些方法绕过,我是否走在正确的轨道上?
任何帮助将不胜感激。
答案 0 :(得分:2)
正如您所说,您需要从UI线程中填充comboBox。从另一个线程访问它的任何尝试都会给您CrossThreadException
。我发现执行此操作的最简单方法是像这样从Task返回信息:
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;
}
}
答案 1 :(得分:-2)
由于错误状态,您正在创建的新线程无法访问ComboBox
,因为它没有在该新线程中实例化。虽然您有异步等待的正确想法。
我认为(这只是一种实现方式),您最好的选择是将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));
}