locked
[solved] block synchronous method while waiting for async method to complete RRS feed

  • Question

  • So, I've written this WPF MessageBox service a while ago:

    https://msgbox.codeplex.com/

    ...and wanted to extend it with a kind of ContentDialog MessageBox service for WPF Desktop applications:

    https://github.com/Dirkster99/MDemo

    ...which is where I am seeming to hit a dead end because the message box service was implemented
    with async methods (I took the code from MahApps.Metro) but my old library was based on non-async
    code and I would like to use the same method signature as before - so I am looking for a way to make
    this new library also work in a non-async context. The problem can be best verified here:

    ...clicking on the red marked button will fire off the code in the DemoViewModel.cs at about line 673
    where I have tried in vain to find a way for making the async message box service work inside a normal
    synchronous context.

    Is it possible to call the asynchronous messagebox within a normal non-async method (I would extend
    the API accordingly if there was a way...)?

    ...or does such an implementation really mean that I cannot use an async method in a non-async context?



    • Moved by DotNet Wang Tuesday, January 10, 2017 2:13 AM
    • Edited by Dirkster999 Wednesday, January 11, 2017 5:43 PM
    Saturday, January 7, 2017 11:29 PM

Answers

  • I asked the people from MahApps.Metro and they gave me the solution Stephen Toub has suggested in your very own space. Here is the github ticket that I've created yesterday and closed now:

    https://github.com/MahApps/MahApps.Metro/issues/2800#issuecomment-271792037

    ...and here is the solution from Stephen:

    The first option is to pump while waiting for the task to complete,
    which you can do in WPF with a method like: public static void WaitWithPumping(this Task task) { if (task == null) throw new ArgumentNullException(“task”); var nestedFrame = new DispatcherFrame(); task.ContinueWith(_ => nestedFrame.Continue = false); Dispatcher.PushFrame(nestedFrame); task.Wait(); } You can then call that method like: task.WaitWithPumping();

    Thanks for your support though. I consider this critical information for the application async/await in WPF so I strongly suggest publishing information like this in a more obvious space like in one of Lucian's next videos, MSDN page on async/await and so forth...

    • Marked as answer by Dirkster999 Wednesday, January 11, 2017 5:43 PM
    Wednesday, January 11, 2017 5:43 PM

All replies

  • If Show is an asynchronous method with a return type of Task<MsgBoxResult> you could get the MsgBoxResult synchronously by calling the GetAwaiter() method of the Task<MsgBoxResult>():

        private MsgBoxResult ShowMessageTest(string message)
            {
                var msg = GetService<IContentDialogService>().MsgBox;
                return msg.Show(message).GetAwaiter().GetResult();
            }

    Alternatively you could block on the Result property of the Task:

        private MsgBoxResult ShowMessageTest(string message)
            {
                var msg = GetService<IContentDialogService>().MsgBox;
                return msg.Show(message).Result;
            }

    The difference is the exception handling: http://stackoverflow.com/questions/17284517/is-task-result-the-same-as-getawaiter-getresult

    Note that blocking on async code is generally a bad idea though as your code may deadlock. Please refer to Stephen Cleary's blog post for more information about this: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html. So you should probably provide a synchronous version of the service method as well.

    If you need any further help on this I suggest that you post or upload a minimal reproducible sample of your issue instead of linking to some GitHub repository with classes that contains almost 1000 lines of code.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Sunday, January 8, 2017 4:28 PM
  • Hi,

    thanks for your suggestions but these hints are not really solving anything for me. The first suggestion that you state is already available in the code repository and should be visible if you follow the DemoViewModel.cs link stated above. I have tried the 2nd proposal about blocking on the Result only and it results in the same situation as the first solution:

    The application hangs most likely because both solutions create a deadlock situation which you could have verified if you would have downloaded the repository :-( The repository, albeit large, is not just any repository but THE repository that show cases the situation I am caught in right now ...

    I am aware of Stephen Toub's blog and Steve Cleary's blog and the Channel 9 videos from Lucian but all of them seem to be short on real solutions that can be applied here. The only vague hint I've seen was in part 3 or 4 of Lucian's videos where he states this could be solved with an own message loop and he refers to Stephen Toub's blog but I was unable to find that solution either.

    Right now I am not necessarily looking for a quick and dirty fix as you suggest above but a fix that will work and is technically sound. It would be great if I could find some sample code that would, for instance, give me an insight on that mysterious message loop that could apparently resolve this for good...?

    Here is the video where I heard Lucian saying that there are solutions to this and it amounts to having your own message loop - I just can't seem to find the resource he is referring to :-(

    https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Async-Library-Methods-Shouldn-t-Lie

    • Edited by Dirkster999 Monday, January 9, 2017 10:24 PM
    Monday, January 9, 2017 7:19 PM
  • Hi Dirkster999,

    Based on your description, your case more related to WPF, I will help move your case to WPF forum for better support.

    Best regards,

    Kristin


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, January 10, 2017 2:12 AM

  • Hi Dirkster999,

    >>with async methods (I took the code from MahApps.Metro) but my old library was based on non-async

    I suggest you can try to use the MahApps.Metro.Controls.Dialogs.

    The following links for your reference.

    MahApps.Metro.Controls.Dialogs:
    http://mahapps.com/controls/dialogs.html

    MahApps, Custom Dialog, MVVM Light, and me:
    http://www.global-webnet.net/IDontKnowAnyBetter/post/2015/11/20/mahapps-custom-dialog-mvvm-light-and-me

    Note: This response contains a reference to a third party World Wide Web site. Microsoft is providing this information as a convenience to you. Microsoft does not control these sites and has not tested any software or information found on these sites; therefore, Microsoft cannot make any representations regarding the quality, safety, or suitability of any software or information found there. There are inherent dangers in the use of any software found on the Internet, and Microsoft cautions you to make sure that you completely understand the risk before retrieving any software from the Internet.

    Best Regards,

    Yohann Lu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, January 10, 2017 12:00 PM
  • >>Right now I am not necessarily looking for a quick and dirty fix as you suggest above but a fix that will work and is technically sound

    That would be to provide a synchronous version of the service method and call this one instead of using the asynchronous method as I suggested in my previous reply.

    An async method should be async 'all the way' which means that you should always await it (except in the Main entry method of an application). If you can't do this for some reason you should use a synchronous version. There is no other "technically sound" solutions than the ones that have been mentioned here.

    Bottom line is that you should not mix synchronous and asynchronous code as this may lead to deadlocks which you have already discovered.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Tuesday, January 10, 2017 6:19 PM
  • Hi Yohann,

    thanks for sharing the link about thr MVVM Light version - I got this to work and
    this approach is interesting indeed but it is also based on async/await functions

    MainViewModel.cs

    private async void ShowDialog()
    {
      await _dialogCoordinator.ShowMetroDialogAsync(this, _dialogView);
    }

    private async void ProcessMessage(string messageContents)
    {
      DialogResult = messageContents;
      await _dialogCoordinator.HideMetroDialogAsync(this, _dialogView);
    }

    - so while it is using the MVVM pattern, its view portion is technically no different from what I already have (as far as the view portion is considered). But thanks for trying to help anyway.
    Tuesday, January 10, 2017 8:35 PM
  • Thanks for sharing this information but I was already aware of this from the blogs and videos cited above. As I wrote before: Lucian makes a different claim in one of his Channel 9 videos - I think that either the video should be changed or his claim should be backed up with a sample project on the message loop solution ...


    • Edited by Dirkster999 Tuesday, January 10, 2017 8:40 PM
    Tuesday, January 10, 2017 8:39 PM
  • I asked the people from MahApps.Metro and they gave me the solution Stephen Toub has suggested in your very own space. Here is the github ticket that I've created yesterday and closed now:

    https://github.com/MahApps/MahApps.Metro/issues/2800#issuecomment-271792037

    ...and here is the solution from Stephen:

    The first option is to pump while waiting for the task to complete,
    which you can do in WPF with a method like: public static void WaitWithPumping(this Task task) { if (task == null) throw new ArgumentNullException(“task”); var nestedFrame = new DispatcherFrame(); task.ContinueWith(_ => nestedFrame.Continue = false); Dispatcher.PushFrame(nestedFrame); task.Wait(); } You can then call that method like: task.WaitWithPumping();

    Thanks for your support though. I consider this critical information for the application async/await in WPF so I strongly suggest publishing information like this in a more obvious space like in one of Lucian's next videos, MSDN page on async/await and so forth...

    • Marked as answer by Dirkster999 Wednesday, January 11, 2017 5:43 PM
    Wednesday, January 11, 2017 5:43 PM