Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Await stops execution of thread and never continues

I have the following:

public async Task<bool> SearchForUpdatesAsync()
{
    return await TaskEx.Run(() =>
    {
    if (!ConnectionChecker.IsConnectionAvailable())
        return false;

    // Check for SSL and ignore it
    ServicePointManager.ServerCertificateValidationCallback += delegate { return (true); };

    var configurations = UpdateConfiguration.Download(UpdateConfigurationFileUri, Proxy);
    var result = new UpdateResult(configurations, CurrentVersion,
        IncludeAlpha, IncludeBeta);

    if (!result.UpdatesFound)
        return false;

    _updateConfigurations = result.NewestConfigurations;
    double updatePackageSize = 0;
    foreach (var updateConfiguration in _updateConfigurations)
    {
        var newPackageSize = GetUpdatePackageSize(updateConfiguration.UpdatePackageUri);
        if (newPackageSize == null)
            throw new SizeCalculationException(_lp.PackageSizeCalculationExceptionText);

        updatePackageSize += newPackageSize.Value;
        _packageOperations.Add(new UpdateVersion(updateConfiguration.LiteralVersion),
            updateConfiguration.Operations);
    }

    TotalSize = updatePackageSize;
    return true;
});
}

As you can see I'm using Microsoft.Bcl. Now in my other class I wrote this code in a normal void:

TaskEx.Run(async delegate
{
    // ...
    _updateAvailable = await _updateManager.SearchForUpdatesAsync();
     MessageBox.Show("Test");
});

The problem I have is that it executes _updateAvailable = await _updateManager.SearchForUpdatesAsync(); and then it doesn't continue the thread, it just stops as if there is nothing after that call. Visual Studio also tells me this after a while: Thread ... exited with code 259, so something seems to be still alive.

I debugged through it to search for any exceptions that could maybe be swallowed, but nothing, everything works fine and it executes the return-statement. And that is what I don't understand, I never see the MessageBox and/or no code beyond this line's being executed. After I talked to some friends, they confirmed that this shouldn't be. Did I make a horrible mistake when implementing async-await?

Thanks in advance, that's actually all I can say about that, I got no more information, I appreciate any tips and help as far as it's possible.

like image 976
Dominic B. Avatar asked Mar 25 '15 17:03

Dominic B.


2 Answers

The main issue that you're having is that you're unnecessarily wrapping your method in TaskEx.Run() and you are probably experiencing deadlock somewhere.

Your method signature is currently:

public async Task<bool> SearchForUpdatesAsync()

This means the following:

async --> Doesn't actually do anything, but provides a "heads up" that await might be called within this method and that this method can be used as a runnable Task.
Task --> This method returns a runnable task that can be run asynchronously on the threadpool
<bool> --> This method actually returns bool when awaited.

The await TaskEx.Run() is unnecessarily since this says run this method and then don't return until after a value is available. This is most likely causing a synchronization problem. Removing this construct will make your method work properly, however you'll notice that now you have no reason to even include the async operator or the Task<T> portion since the method is actually synchronous anyway. Usually you're only going to use async identifier on the method signature if you have methods that you are going to call await on them.

Instead you have two options.

  1. Whenever you want to call SearchForUpdates() you can wrap this in a Task<bool>.Run() to run it asynchronously (or the Bcl equivalent)
  2. Since you are using WinForms you might be better off using a BackgroundWorker and just calling this method within it.

Regarding using the async-await pattern I think that this is a great article to use to make sure you're following best practices: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

like image 69
JNYRanger Avatar answered Oct 08 '22 18:10

JNYRanger


The best practice is to have async all the way through your layers, and then call await or less desirably .Wait() / .Result at the final use site.

Also, try to keep your UI calls separate from the backend work, since you can run into synchronicity/thread-context issue.

public class WinFormsCode
{
    private async Task WinsFormCodeBehindMethodAsync()
    {
        var updatesAvailable = await _updateManager.SearchForUpdatesAsync();
        MessageBox.Show("Updates Available: " + updatesAvailable.ToString());
    }

    private void WinsFormCodeBehindMethodSync()
    {
        var updatesAvailable = _updateManager.SearchForUpdatesAsync().Result;
        MessageBox.Show("Updates Available: " + updatesAvailable.ToString());
    }
}

public class UpdateManager
{
    public async Task<bool> SearchForUpdatesAsync()
    {
        return true;
    }
}
like image 21
Silas Reinagel Avatar answered Oct 08 '22 18:10

Silas Reinagel