C# WPF Отмена асинхронной функции
Я все еще довольно новичок в C# WPF и работаю с асинхронными функциями. Это то, что у меня есть
private void btnGetAccount(object sender, RoutedEventArgs e) {
try {
var found = Task<bool>.Factory.StartNew(() => SearchForAccount());
await found;
}
catch ....
}
а также
private bool SearchForAccount() {
Dispatcher.Invoke(() => { //UI Updates }
AnotherFunctionCall();
return true;
}
Проблема в том, что время ожидания функции SearchForAccount истекает. Я не понял, что является причиной этого, так как никаких ошибок не было. Я хотел бы реализовать кнопку, которая позволяет отменить этот вызов функции. Я пытался возиться с CancellationTokenSource, но, похоже, он не работает так, как я.
Любая помощь или предложения будут с благодарностью.
Спасибо! RealityShift
РЕДАКТИРОВАТЬ:
Вот моя попытка с CancellationToken
private void btnGetAccount(object sender, RoutedEventArgs e) {
CancellationTokenSource cts = new CancellationTokenSource();
try {
cts.CancelAfter(200);
var found = Task<bool>.Factory.StartNew(() => SearchForAccount(), cts.Token);
await found;
}
catch (OperationCanceledException ex) {
SetStatusLabel("Cancel done.");
}
}
Я также попробовал что-то вроде этого (не могу вспомнить точно, и это не в истории отмены сейчас):
private void btnGetAccount(object sender, RoutedEventArgs e) {
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
try {
cts.CancelAfter(200);
var found = Task<bool>.Factory.StartNew(() => SearchForAccount(token), cts.Token);
await found;
}
catch (OperationCanceledException ex) {
SetStatusLabel("Cancel done.");
}
}
Я передал токен в функцию, но я не помню, что я сделал с функцией. В обоих случаях ничего не происходит для отмены, пока не завершится вызов функции SearchForAccount (если он вернется). Если он не возвращается, то он застрял, именно поэтому я хочу кнопку отмены.
Быстрый запуск: программа запускается. Пользователь может ввести имя пользователя и нажать кнопку поиска. В результате поиска будет выполнен поиск, чтобы определить, существует ли учетная запись в домене, и какие-либо сведения о ней. После завершения поиска результаты публикуются в сетке данных.
Проблема: при нажатии на поиск иногда (редко) он будет продолжать поиск бесконечно и никогда не вернется. Что-то, чтобы вернуть сообщение о превышении времени было бы здорово.
2 ответа
Вы должны отделить логику получения данных и обновления пользовательского интерфейса, в тот момент, когда вы используете диспетчерский вызов, вы замораживаете поток пользовательского интерфейса. Используйте что-то более похожее на это.
private CancellationTokenSource cts;
private void btnGetAccount(object sender, RoutedEventArgs e)
{
try
{
if (cts != null)
{
cts.Cancel();
}
cts = new CancellationTokenSource();
var token = cts.Token;
Task.Factory.StartNew(
() =>
{
var found = SearchForAccount();
if (!token.IsCancellationRequested)
{
Dispatcher.Invoke(
() =>
{
SetStatusLabel(found ? "Found" : "Not found");
});
}
},
token);
}
catch (OperationCanceledException ex)
{
SetStatusLabel("Cancel done.");
}
}
Я только знаю, как вы можете отменить выполнение Task
, Может быть, это будет полезно.
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Task myTask = Task.Factory.StartNew(() =>
{
while (true)
{
token.ThrowIfCancellationRequested();
// loop's body
}
}, token);
// cancellation
cts.Cancel();
или же
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Task myTask = Task.Factory.StartNew(() =>
{
while (true)
{
if(token.IsCancellationRequested)
{
return;
}
// loop's body
}
}, token);
// cancellation
cancelToken.Cancel(false);
Схема исполнения выглядит так:
//property of the class
private readonly CancellationTokenSource cts = new CancellationTokenSource();
private void btnDo(object sender, RoutedEventArgs e) {
var found = Task.Factory.StartNew(() =>
{
try
{
while (true)
{
cts.Token.ThrowIfCancellationRequested();
//loop's body
}
}
catch (OperationCanceledException ex)
{
SetStatusLabel("Cancel done.");
}
}, cts.Token);
}
private void btnCancel(object sender, RoutedEventArgs e) {
//cancelling
cts.Cancel();
}