John Livermore
John Livermore

Reputation: 31313

C# async calls and realm instances

I am using Realm with a Xamarin Forms project, and I have read about how realm entity instances can't be shared across threads.

Given the following code, is using the route obtained in line 100, and then accessed again on line 109 after the awaited call on 104, dangerous?

I am new to using Realm, but if this is true, then one must get a new instance of the Realm and any object being worked with after any/every awaited call. Seems onerous...

enter image description here

Upvotes: 0

Views: 923

Answers (1)

SushiHangover
SushiHangover

Reputation: 74174

is using the route obtained in line 100, and then accessed again on line 109 after the awaited call on 104, dangerous?

Yes, on the next foreach iteration, you will end up with a different managed thread, and Realm will throw an different thread access exception.

The key is to use a SynchronizationContext so your await continuations are on the same thread (and, of course, since you will be in a different thread, skip the use of the Realm-based async methods)

Using Stephen Cleary's Nito.AsyncEx (he is the king of sync contexts 😜)

re: how can i force await to continue on the same thread?

var yourRealmInstanceThread = new AsyncContextThread();
await yourRealmInstanceThread.Factory.Run(async () =>
{
    var asyncExBasedRealm = Realm.GetInstance();
    var routes = asyncExBasedRealm.All<UserModel>();
    foreach (var route in routes)
    {
        // map it
        // post it
        await Task.Delay(TimeSpan.FromMilliseconds(1)); // Simulate some Task, i.e. a httpclient request.... 
        // The following continuations will be executed on the proper thread
        asyncExBasedRealm.Write(() => route.Uploaded = true);
    }
});

Using SushiHangover.RealmThread

I wrote a simple SynchronizationContext for Realm awhile back, it works for my needs and has a specialized API for Realm.

using (var realmThread = new RealmThread(realm.Config))
{
    await realmThread.InvokeAsync(async myRealm =>
    {
        var routes = myRealm.All<UserModel>();
        foreach (var route in routes)
        {
            // map it
            // post it
            await Task.Delay(TimeSpan.FromMilliseconds(1)); 
            // The following continuations will be executed on the proper thread
            myRealm.Write(() => route.Uploaded = true);
        }
    });
}

Note: For someone that does not understand SynchronizationContext well, I would highly recommend using Nito.AsyncEx as a generic solution that is well supported and due to the fact that is from Stephen Cleary... I use it in a vast majority of my projects.

Upvotes: 4

Related Questions