Вы должны спросить себя, нужно ли вам общее решение ( другое - Аллен Браун ), или если вам это нужно только для этой цели. Если вы действительно нуждаетесь в этом только один раз, сделайте это простым способом.
На стороне примечания, при объединении списков в коде VBA, воспользуйтесь трюком, которым меня научил давний гуру доступа Trevor Best , и это нужно придерживать разделитель в начале каждого значения, а затем использовать Mid (), чтобы отключить его. Вместо этого внутри вашей петли через дочерние записи:
If Len(strOutput) = 0 Then
strOutput = NewValue
Else
strOutput = strOutput & ", " & NewValue
End If
... используйте это внутри цикла:
strOutput = strOutput & ", " & NewValue
... и затем, когда вы выходите из цикла , отделите главный разделитель:
strOutput = Mid(strOutput, 3)
Это имеет последствия повсюду и упрощает код для конкатенации во множестве контекстов.
Вы завершили свой async
вызов в задании. Это побеждает всю машину состояний и заканчивается ненужным использованием потока потоков.
Вызов и любые продолжения заканчиваются в другом потоке и, следовательно, не могут получить доступ к интерфейсу без маршалинга.
Как минимум, вы должны делать это, если вам абсолютно необходимо использовать async
void
это просто ожидание задачи, а не ее завершение
private async void ActivateAsyncTicToc()
{
try
{
IsTimerStartAsync = !IsTimerStartAsync;
await AsyncTicToc();
}
catch (Exception e)
{
// make sure you observe exceptions in async void
}
}
Еще лучше, пусть async
распространяется с использованием async Task
private async Task ActivateAsyncTicToc()
{
IsTimerStartAsync = !IsTimerStartAsync;
await AsyncTicToc();
}
Что в любом случае выглядит подозрительно, вы, вероятно, должны быть делая это (Eliding async
и await
) и просто передавая задачу кому-то еще, кто будет ждать. это небольшой прирост производительности
private Task ActivateAsyncTicToc()
{
IsTimerStartAsync = !IsTimerStartAsync;
return AsyncTicToc();
}
на самом деле это минное поле, и я буду здесь весь день
Вы должны начать читать о async
и await
Прежде всего, вы можете использовать await Task.Delay(10)
вместо Thread.Sleep(10)
и изменить свой метод AsyncTicToc
на async Task
без запуска потока. Это сделает изменение элемента управления в потоке пользовательского интерфейса (контекст, от которого вы ожидали).
Во-вторых, чтобы обновить пользовательский интерфейс из других потоков, если вы не хотите использовать диспетчер напрямую, вы можете использовать абстракцию над ним: класс SynchronizationContext . См. Больше информации здесь .
Пример: SynchronizationContext.Current.Post(action, actionParameter);
В-третьих, более уместно использовать модель представления и привязку. Если вы используете WPF, он синхронизирует потоки для изменений свойств для вас.
В то время как есть возможность «выйти» из другого потока в поток пользовательского интерфейса, используя Dispatcher
или SynchronizationContext
, я настоятельно рекомендую этого не делать. Это потому, что это делает вашу логику менее тестируемой и более привязанной к ее среде. В случае Dispatcher
он сильно привязан к работе в среде WPF; SynchronizationContext
лучше, но он все еще привязан к работе в какой-то среде.
При таком подходе у вас есть зависимость от вашей логики до потока пользовательского интерфейса, например:
Код пользовательского интерфейса => фоновая логика потока => Поток пользовательского интерфейса
Вместо этого используйте IProgress<T>
и Progress<T>
из фоновой логики потока, чтобы сообщать отчеты о ходе выполнения своему вызывающему, который решает, как отображать эти отчеты о ходе выполнения. Тогда ваша зависимость выглядит следующим образом:
UI code => логика фонового потока
и ваша логика фонового потока не зависит от наличия потока UI. Это делает его более убедительным и более тестируемым.