Cannot change thread mode after it is set

July 06, 2015

I recently published this article on using asynchonous methods within a Windows Game, and this article on enabling consumables within a Windows Game

However, I encountered a problem when I tried to use these two methods in combination. My code looked something like this:



var result = MessageBoxHelper.MsgBox.ShowAsync(string.Format("You do not have sufficient widgets for this.\\n" +
    "Would you like to purchase more?"), MessageBoxButton.YesNo).ContinueWith(async (answer) =>
{
    if (answer.Result == MessageBoxResult.Yes)
    {
        bool purchTask = await Purchase.RequestProductPurchase(WIDGETS);

However, when I ran this; I got the following exception:

cannotchangethread

This is a marginally documented error:

By Microsoft and by Stack Overflow

I could call the function Purchase.RequestProductPurchase directly, but not, for some reason, in conjunction with ContinueWith. I tried this combination in a basic console application:



        static void Main(string[] args)
        {
            continueWithTest();
        }

        private static void continueWithTest()
        {
            MyAsyncFunc().ContinueWith(async (a) =>
            {
                Console.WriteLine("test");
                await Task.Delay(2000);                
            }).Wait();
            Console.ReadLine();
        }
        static async Task MyAsyncFunc()
        {
            await Task.Delay(2000);
            Console.WriteLine("Test1");
    }

And it worked fine. Based on what is available to read on the web, it looks like the threading model is, in some way, different when using ContinueWith. Although, it would appear that if you specify the TaskScheduler.Current, you should be able to use this in place of await, but that seems to make no difference.

The fix, as so many people are quick to point out, is use await:



        private void MakePurchase(int cost, Action onSuccess, bool interactive)
        {
            if (App.settings.CashPot.Total > cost)
            {
                App.settings.CashPot.Total -= cost;
                onSuccess.Invoke();                
            }
            else if (interactive)
            {
                InsufficientFunds(cost, onSuccess);
            }
        }


        private async void InsufficientFunds(int cost, Action onSuccess)
        {
            var result = await MessageBoxHelper.MsgBox.ShowAsync(string.Format("You do not have sufficient funds for this.\\n" +
                "Would you like to purchase additional cash?"),
                "Insufficient Funds", MessageBoxButton.YesNo);                
                            
            if (result == MessageBoxResult.Yes)
            {
                bool purchTask = await Purchase.RequestProductPurchase(WIDGETS);

                MakePurchase(cost, onSuccess, false);
            }
        }



Profile picture

A blog about one man's journey through code… and some pictures of the Peak District
Twitter

© Paul Michaels 2024