0

UPDATE:

public MobileServiceUser CurrentMsUser { get; private set; } 

I have an instance property CurrentMsUser which is populated and I'm trying to reference it within an async method, but for some reason it is null in the method. I've set a breakpoint on the property setter and it never is set to null, so I'm fairly certain the property never becomes null. After the async method returns it's present again. It seems as soon as I'm within the anonymous async method I can't access the property. Here is my method:

public async Task CreateOrRetrieveAppUserAsync()
{
    await Task.Run (async () => {
        try {
            var usersCollection = await _userTable.ToCollectionAsync ();
            var users = usersCollection.
                Where(x =>
                    x.FacebookToken == CurrentMsUser.UserId).
                ToList();
            if (users.Count == 1) {
                CurrentRwUser = users [0];
            } else {
                CurrentRwUser = new User { 
                    FacebookToken = CurrentMsUser.UserId, 
                    GoogleToken = "test",
                    TwitterToken = "test",
                    MicrosoftToken = "test",
                    Email = "test@gmail.com",
                    FacebookId = App.FacebookProvider.GetCurrentUserId (),
                    Name = App.FacebookProvider.GetCurrentUserName ()
                };
                await InsertUserAsync (CurrentRwUser);
                await SyncAsync();
            }
        } catch (Exception ex) {
            Debug.WriteLine ("CreateOrRetrieveAppUser failed: {0}", ex.Message);
        }
    }).ConfigureAwait(continueOnCapturedContext:false);
}
Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
  • 2
    Out of curiosity, why are you wrapping all of that logic in a new task? – Daniel Mann Sep 04 '14 at 17:46
  • Because the method is called from the UI thread –  Sep 04 '14 at 17:49
  • 1
    Where *exactly* is it null? It seems this is setting the value of `CurrentRwUser` so I would have assume it was null before you called this method? Are you saying it's still null *after* the assignment? How are you determining that? Is it null in `InsertUserAsync`? If you threw in a `Debug.WriteLine(CurrentRwUser.UserId)` at the end of that method does it throw a `NullReferenceException`? – Matt Burland Sep 04 '14 at 17:50
  • The thread it's running on should be irrelevant. If the long-running operations are async, the UI will remain responsive while they're running. – Daniel Mann Sep 04 '14 at 17:51
  • EDIT: Typo on my part. It's CurrentMsUser that is becoming null, not CurrentRwUser. @MattBurland - If I break on the opening brace of this method it is present, but as soon as I step into the anonymous method within the Task.Run call, it's null. If I run a debug statement like you suggest against CurrentMsUser.UserId once the method returns it is not null. –  Sep 04 '14 at 17:55
  • @DanielMann To my understanding, just because a method is marked with async does not mean it runs on it's own thread, it's your responsibility to start it on a new thread, hence Task.Run. Since this method is called from my UI thread, I do the work on a new thread so the UI can update.. –  Sep 04 '14 at 17:57
  • You write that you check the setter. The problem may be the getter (e.g. using thread local storage). Also, is the variable null if it is never set? – Peter Sep 04 '14 at 17:58
  • Post CurrentMsUser property. – comdiv Sep 04 '14 at 17:59
  • @winnicki One of the big advantages of async methods is that you don't have to mess around with new threads in order to keep the UI responsive. The work is done asynchronously, without threads, while keeping the UI responsive. Try it without the `Task.Run`. See what happens. – Daniel Mann Sep 04 '14 at 18:05
  • @DanielMann The reason I put Task.Run is because it was blocking the UI thread in the first place. –  Sep 04 '14 at 18:07

1 Answers1

3

It appears that that variable is thread local, which is why its value is only accessible in your main thread.

You should grab the value of that variable in your main thread before moving to the background thread to avoid this problem.

public async Task CreateOrRetrieveAppUserAsync()
{
    var userId = CurrentMsUser.UserId;
    await Task.Run (async () => {
    //...
    //use userId here
    });
}

Of course, given that you don't appear to have any long running CPU bound work, and all of the work that you have here is already asynchronous task based IO work, you should be able to simply remove the Task.Run call entirely, allowing what little CPU bound work you do have to run in the UI thread, without blocking it at all due to the long running operations all already being asynchronous.

Oh, and since you only need the very first item of usersCollection you should use First, not ToList, to avoid materializing the entire result set into memory. You should also perform that First and your filtering before calling ToCollectionAsync to, if at all possible, do all of this filtering through the query provider rather than materializing the entire collection into your application.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • I have an inkling that `App.FacebookProvider.GetCurrentUserId` and `App.FacebookProvider.GetCurrentUserName` are long-running and not async. Or possibly that `usersCollection` is huge. – Daniel Mann Sep 04 '14 at 18:12
  • This works but doesn't really answer why the instance variable becomes null in the first place. Thanks –  Sep 04 '14 at 18:14
  • @DanielMann They aren't async and also not long-running.. just grabbing from an already populated variable. usersCollection is also only about 20 rows.. –  Sep 04 '14 at 18:17
  • @DanielMann If those two methods are actually long running they can be wrapped in their own `Task.Run` calls, although I wouldn't expect them to be. As for the collection, see my edit. He's only actually trying to get the first item, and a trivial change to avoid trying to process the whole result set on the application side would mean that the size of the collection wouldn't affect the CPU bound work. – Servy Sep 04 '14 at 18:18
  • @Servy Thanks for the First tip! Editing it now. –  Sep 04 '14 at 18:18
  • @winnicki If it's a thread local variable it absolutely explains why it's null in a non-UI thread and populated in the UI thread. That's the whole point of thread local content, each thread has their own copy of the variable. – Servy Sep 04 '14 at 18:19
  • @Servy All valid points (I thought the same things myself). I was just pointing them out as possibilities. – Daniel Mann Sep 04 '14 at 18:19
  • 1
    @Servy Now I'm just confused. I just finished reading this answer by Eric Lippert http://stackoverflow.com/questions/18770414/why-can-a-local-variable-be-accessed-in-another-thread-created-in-the-same-class and it sounds like I should be able to access this property across threads. If it's a public property of the class why does that make it thread local? –  Sep 04 '14 at 18:30
  • @winnicki It would be thread local if it was marked as thread local. It's of course also theoretically possible for it to be modified in the UI thread in such a way as to not be visible in the non-UI thread due to a lack of synchronization mechanisms. Without actually seeing the code, it's hard to say. This is why, as Eric himself often says, you should avoid accessing memory from multiple threads whenever possible. It's just one big giant headache. – Servy Sep 04 '14 at 18:34
  • @Servy Thanks. You're right. I'll take that advice and avoid it in the first place. –  Sep 04 '14 at 18:57