简体   繁体   中英

C# WPF Cancellation of async function

I'm still fairly fresh to C# WPF and have been working with async functions. This is what I have

private void btnGetAccount(object sender, RoutedEventArgs e) {
    try {
        var found = Task<bool>.Factory.StartNew(() => SearchForAccount());
        await found;
    }
    catch .... 
}

and

private bool SearchForAccount() {
    Dispatcher.Invoke(() => { //UI Updates }
    AnotherFunctionCall();
    return true;
}

The issue is, sometimes the SearchForAccount function times out. I haven't figured out what is causing it since no errors get thrown. I would like to implement a button that allows this function call to cancel. I've tried messing with CancellationTokenSource but it doesn't seem to work the way I am doing it.

Any help or suggestions would be greatly appreciated.

Thanks! RealityShift

EDIT:

Here is my attempt with 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.");
    }
}

I also tried something like this (can't remember exactly and it's not in undo history now):

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.");
    }
}

I passed the token into the function but I don't remember what I did with the function. In both cases, nothing happens for the cancel until after the SearchForAccount function call returns (if it returns). If it doesn't return, then it's stuck which is exactly why I want a cancel button.

Quick run-down: Program launches. User can type in a username and hit search. The search will then search to see if the account exists on the domain and some details on the account. After the search is done, the results are posted to a data grid.

The problem: When hitting search, sometimes (rarely) it will continue to search indefinitely and never return. Something to return a timeout message would be great.

I only know how you can cancel executing Task . May be it will be helpful.

CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;

Task myTask = Task.Factory.StartNew(() =>
{
    while (true)
    {
        token.ThrowIfCancellationRequested();
        // loop's body
    }
}, token);

// cancellation
cts.Cancel();

or

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);

The schema of execution looks like this:

//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();
}

You should separate the logic of getting the data and updating the UI, at the moment you use the dispatcher invoke you are freezing the UI thread. Use something more similar to this.

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.");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM