Как вы говорите, вам нужно заполнить comboBox из потока пользовательского интерфейса. Любая попытка получить к нему доступ из другого потока даст вам 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.
Вот несколько простых изменений, которые передают результаты, полученные в режиме реального времени, обратно пользователю по мере выполнения задачи, связанной с ЦП.
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;
}
}