locked
Async Await Issues RRS feed

  • Question

  • I'm trying to port some event based async code to the new Async/Await pattern but it seems to be blocking my UI thread.

    A couple of questions to hopefully get to the bottom of this....
    I have a timer that in my old pattern would run a Method and the completedevent would be handled elsewhere.

    Looking at the below Methods.....

    void _guiUpdateTimer_Tick(object sender, object e) { SomeAsyncMethod1(); } private async Task SomeAsyncMethod1() { await someayncmethodMixed(); await someasyncmethodNonAwait(); } private async Task someayncmethodMixed() //Mixed Awaited And Non Awaited Methods { await Method1Async(); await Method2Async(); Method3(); } private async Task someasyncmethodNonAwait() //Non Awaited Methods Only {

    somenonawaitedsyncmethod(); }

    1) In someayncmethodMixed()  will Method3 be run in a seperate thread or will it revert back to the UI thread?
    2) In someasyncmethodNonAwait() will the somenonawaitedsyncmethod() be run in a separate thread or UI thread?

    To give some context I have two methods I am trying to port.  One method has no awaits at all. It simply runs for a long time and is CPU intensive.  The second method has a mixture of Async tasks (File access and Web Calls) as well as non Async methods. All the calls were Synchronous calls such as Httpwebrequest.getresponse() but have been ported to their new Async counterparts such as Httpwebrequest.begingetresponse()

    I'm trying to run both these methods, one after another, on a separate thread.  The call is coming from a Timer Tick event. With the above solution my UI thread is blocked whist they are running.


    • Edited by StormENT Tuesday, August 28, 2012 1:16 PM
    Tuesday, August 28, 2012 1:13 PM

Answers

  • <Page
        x:Class="App1.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App1"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="myButton" Content="GO" Click="Button_Click_1"/>
        </Grid>
    </Page>

    ---

    using System;
    using System.Threading.Tasks;
    using Windows.UI.Core;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Navigation;
    
    
    namespace App1
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
            private async void Button_Click_1(object sender, RoutedEventArgs e)
            {
                // Variables
                Windows.UI.Core.CoreDispatcher dispatcher;
    
                // Either or the following
                // dispatcher = this.Dispatcher;
                dispatcher = Window.Current.Content.Dispatcher;
    
                // Either or the following
                // await Task.Run(async () => await Method1("Argument 1", dispatcher));
                // await Task.Run(async () => await Method2("Argument 2"));
                // await Task.Run(async () => await BackgroundClass.Method3("Argument 3", dispatcher, this.myButton));
                await Task.Run(async () => await BackgroundClass.Method4("Argument 4", this.myButton));
            }
            private async Task Method1(string anArgument, Windows.UI.Core.CoreDispatcher dispatcher)
            {
                string s = String.Format("Method1: a long threadpool compute:{0}", anArgument);
                await dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
                {
                    this.myButton.Content = s;
                });
            }
            private async Task Method2(string anArgument)
            {
                string s = String.Format("Method2: a long threadpool compute:{0}", anArgument);
                await this.myButton.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
                {
                    this.myButton.Content = s;
                });
            }
        }
        public class BackgroundClass
        {
            public static async Task Method3(string anArgument, Windows.UI.Core.CoreDispatcher dispatcher, Button button)
            {
                string s = String.Format("Method3: long threadpool compute:{0}", anArgument);
                await dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
                {
                    button.Content = s;
                });
            }
            public static async Task Method4(string anArgument, Button button)
            {
                string s = String.Format("Method4: long threadpool compute:{0}", anArgument);
                await button.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
                {
                    button.Content = s;
                });
            }
        }
    }
    

    ---

    The original code contained Windows.Current.Content.Dispatcher as a 'direct' parameter. Since it is exploited [resolved] on another thread, the result is Null. Hence it must first be assigned to a local 'dispatcher' variable before invoking Method1/2. Sorry for that silly mistake.



    • Marked as answer by StormENT Tuesday, August 28, 2012 5:40 PM
    • Edited by ForInfo Wednesday, August 29, 2012 5:43 AM
    Tuesday, August 28, 2012 5:11 PM

All replies

  • - Assuming '_guiUpdateTimer_Tick' is a handler method triggered from a UIElement, everything above will run on the UIThread 

    - If you want to delegate to the ThreadPool in the above, you need constructions like:

    await Task.Run(async () => await Method4(anArgument));
    await ThreadPool.RunAsync(delegate { myReturnValue = Compute(anArgument); });

    -

    If in the ThreadPool Method4 (e.g.) you also want to modify values of a UIElement (e.g. myTextBox.Text), you'll need to pass the Dispatcher of the UIThread as an argument. For example: 

    - await Task.Run(async () => await Method4(anArgument, Dispatcher [or Window.Current.Content.Dispatcher]));

    async void Method4(string anArgument, Windows.UI.Core.CoreDispatcher dispatcher)
    { 
    string s = "A value"; // a long computation
    await dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate { myTextBox.Text = s; }); 
    }




    • Edited by ForInfo Tuesday, August 28, 2012 4:52 PM
    Tuesday, August 28, 2012 2:03 PM
  • Thanks Forinfo - I'm nearly there but have some strange results.
    This does not run CheckForNewFiles()

    Task.Run(async () => await checkForNewFiles(Window.Current.Content.Dispatcher));
                    

    But works without the Dispatcher

    Task.Run(async () => await checkForNewFiles());

    As does this with the Dispatcher but obviously not on a different thread

    checkForNewFiles(Window.Current.Content.Dispatcher);
    I can't see any errors being thrown,  It simply does not appear to run.

    I'm using Windows.UI.Core.CoreDispatcher dispatcher in my Method.
    • Edited by StormENT Tuesday, August 28, 2012 5:10 PM
    Tuesday, August 28, 2012 3:10 PM
  • <Page
        x:Class="App1.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App1"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="myButton" Content="GO" Click="Button_Click_1"/>
        </Grid>
    </Page>

    ---

    using System;
    using System.Threading.Tasks;
    using Windows.UI.Core;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Navigation;
    
    
    namespace App1
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
            private async void Button_Click_1(object sender, RoutedEventArgs e)
            {
                // Variables
                Windows.UI.Core.CoreDispatcher dispatcher;
    
                // Either or the following
                // dispatcher = this.Dispatcher;
                dispatcher = Window.Current.Content.Dispatcher;
    
                // Either or the following
                // await Task.Run(async () => await Method1("Argument 1", dispatcher));
                // await Task.Run(async () => await Method2("Argument 2"));
                // await Task.Run(async () => await BackgroundClass.Method3("Argument 3", dispatcher, this.myButton));
                await Task.Run(async () => await BackgroundClass.Method4("Argument 4", this.myButton));
            }
            private async Task Method1(string anArgument, Windows.UI.Core.CoreDispatcher dispatcher)
            {
                string s = String.Format("Method1: a long threadpool compute:{0}", anArgument);
                await dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
                {
                    this.myButton.Content = s;
                });
            }
            private async Task Method2(string anArgument)
            {
                string s = String.Format("Method2: a long threadpool compute:{0}", anArgument);
                await this.myButton.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
                {
                    this.myButton.Content = s;
                });
            }
        }
        public class BackgroundClass
        {
            public static async Task Method3(string anArgument, Windows.UI.Core.CoreDispatcher dispatcher, Button button)
            {
                string s = String.Format("Method3: long threadpool compute:{0}", anArgument);
                await dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
                {
                    button.Content = s;
                });
            }
            public static async Task Method4(string anArgument, Button button)
            {
                string s = String.Format("Method4: long threadpool compute:{0}", anArgument);
                await button.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
                {
                    button.Content = s;
                });
            }
        }
    }
    

    ---

    The original code contained Windows.Current.Content.Dispatcher as a 'direct' parameter. Since it is exploited [resolved] on another thread, the result is Null. Hence it must first be assigned to a local 'dispatcher' variable before invoking Method1/2. Sorry for that silly mistake.



    • Marked as answer by StormENT Tuesday, August 28, 2012 5:40 PM
    • Edited by ForInfo Wednesday, August 29, 2012 5:43 AM
    Tuesday, August 28, 2012 5:11 PM
  • Thanks ForInfo - My

    async void _guiUpdateTimer_Tick(object sender, object e)
    {
    }

    Wasn't marked as async.  As soon as it was and I awaited Task. Run it all started working. I have an issue accessing some of the objects from the different thread but I'll start a new question for that.

    Tuesday, August 28, 2012 5:40 PM