Forum Xamarin.iOS

Strange behaviour of InvokeOnMainThread

Bartosz_CicheckiBartosz_Cichecki DKMember ✭✭
edited April 2016 in Xamarin.iOS

Hi, I am experiencing a strange behaviour with InvokeOnMainThread.
In particular I have a function that is called (with a very small temporal distance) by two different threads, and in which I need to update some UI controls so I do it in the Action passed to InvokeOnMainThread. The strange thing is that the two Actions are not run sequentally, so one after another, but "concurrently".
In order to make things clearer, let us consider the following function, that has a similar structure to mine, where InnerFunction contains the methods that modify the UI controls.

    void OuterFunction(object sender, EventArgs e)
    {
        ...

        InvokeOnMainThread(() =>
            {
                var guid = Guid.NewGuid();

                Console.WriteLine("STEP 1" + " GUID: " + guid);

                InnerFunction();

                Console.WriteLine("STEP 2" + " GUID: " + guid);
            });
    }

Let's say we have two Threads, ThreadA and ThreadB and that ThreadA calls OuterFunction some milliseconds before ThreadB. What I would expect is that the action passed to InvokeOnMainThread by ThreadA will finish its execution before the Action passed by ThreadB starts executing. Practically an output like this:

 STEP 1 GUID_A
 STEP 2 GUID_A
 STEP 1 GUID_B
 STEP 2 GUID_B

Instead, I am observing that the Action passed by ThreadB starts executing before the other Action has ben completed, so I get an output like the following:

 STEP 1 GUID_A
 STEP 1 GUID_B
 STEP 2 GUID_A
 STEP 2 GUID_B

or even:

 STEP 1 GUID_A
 STEP 1 GUID_B
 STEP 2 GUID_B
 STEP 2 GUID_A

How is this behaviour happening?

[I have edited the answer so I have attached a project that shows the problem]

Answers

  • JohnHJohnH GBMember ✭✭✭✭✭
    edited April 2016

    Interesting, if you log out the thread id at the same time I presume they all say 1, right?
    If you replace the call to InnerFunction with a threadbound delay, does it behave the same? InvokeOnMainThread should only occur when the UI thread goes back to its event processing loop, so maybe something in your InnerFunction is allowing that...
    [EDIT] I could be talking doodoo, the Xamarin documention on this says InvokeOnMainThread does not return until it has completed the action...

  • Bartosz_CicheckiBartosz_Cichecki DKMember ✭✭

    I have attached a sample project that exhibits the issue. I have managed to understand that the cause of the behaviour is related to a method I have created (ToNSAttributedString in the attached project), but I am not sure how.

  • Bartosz_CicheckiBartosz_Cichecki DKMember ✭✭
    edited April 2016

    Yes, if I log the id they all say 1. I am not sure about what do you mean by "threadbound delay", but if instead of the call to the function, I do idle work (like having a for loop with ten thousand loops), the output is as expected.
    But if the inner function is the following one, I have problems:

        public static NSAttributedString ToNSAttributedString(this string str, NSDocumentType type, UIFont font = null)
        {
            try
            {
                if (string.IsNullOrEmpty(str))
                {
                    return null;
                }
    
                var data = NSData.FromString(str);
    
                var options = new NSAttributedStringDocumentAttributes();
                options.DocumentType = type;
                var _unused = new NSDictionary();
                var error = new NSError("Convert to NSAttributedString error.".ToNSString(), 887);
                var attrstr = new NSAttributedString(data, options, out _unused, ref error);
                if (error != null)
                {
                    return null;
                }
    
                if (font != null)
                {
                    var attrstrCopy = new NSMutableAttributedString(attrstr);
                    attrstrCopy.AddAttribute(UIStringAttributeKey.Font, font, new NSRange(0, attrstr.Length));
                    return attrstrCopy;
                }
    
                return attrstr;
            }
            catch (Exception e)
            {
                return null;
            }
        }
    

    One can for example call it in the following way:

          const string htmlString = "<html>\n    <head>\n        <title>Title</title>\n    </head>\n    <body>\n        <div align=”center”>Hello World!</div>\n    </body>\n</html>";
    
          htmlString.ToNSAttributedString(NSDocumentType.HTML);
    

    And this causes the unexpected behaviour. I am attaching again the project, because I deleted the last attachment. If you run the project and click the button, you can see that the order of execution is not as expected.
    And, yes, the documentation says that InvokeOnMainThread is blocking, and that is true. Unfortunately this doesn't help in my case

Sign In or Register to comment.