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();
}
Другие вопросы по тегам