Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sharing scope across awaits

I have a UserScope class which functions similarly to TransactionScope, i.e., it stores the current state in a thread local. This of course doesn't work across calls to await, and neither did TransactionScope until TransactionScopeAsyncFlowOption was added in .NET 4.5.1.

What alternative to thread local can I use so that UserScope can be used the same in single-threaded and multi-threaded scenarios? (If I had 4.5.1 installed I'd decompile to see how TransactionScope does it.) This is a simplified version of what I have:

class User {
    readonly string name;

    public User(string name) {
        this.name = name;
    }

    public string Name {
        get { return this.name; }
    }
}

class UserScope : IDisposable {
    readonly User user;

    [ThreadStatic]
    static UserScope currentScope;

    public UserScope(User user) {
        this.user = user;
        currentScope = this;
    }

    public static User User {
        get { return currentScope != null ? currentScope.user : null; }
    }

    public void Dispose() {
        ///...
    }
}

And this is a test I'd expect to work:

static async Task Test() {
    var user = new User("Thread Flintstone");
    using (new UserScope(user)) {
        await Task.Run(delegate {
            Console.WriteLine("Crashing with NRE...");
            Console.WriteLine("The current user is: {0}", UserScope.User.Name);
        });
    }
}

static void Main(string[] args) {
    Test().Wait();
    Console.ReadLine();
}
like image 630
Daniel Avatar asked Mar 12 '14 21:03

Daniel


1 Answers

In .NET 4.5 full framework, you can use the logical call context for this:

static async Task Test()
{
    CallContext.LogicalSetData("Name", "Thread Flintstone");
    await Task.Run(delegate
    {
        //Console.WriteLine("Crashing with NRE...");
        Console.WriteLine("The current user is: {0}", CallContext.LogicalGetData("Name"));
    });
}

static void Main(string[] args)
{
    Test().Wait();
    Console.ReadLine();
}

However, you should only store immutable data in the logical call context. I have more details on my blog. I've been meaning to wrap this up into an AsyncLocal<T> library, but haven't (yet) found the time.

like image 118
Stephen Cleary Avatar answered Sep 20 '22 20:09

Stephen Cleary