Thread Safe Collection

ON's Avatar

ON

19 Sep, 2017 04:11 PM

Hi,

I have a need to make a collection (in this case a Stack<string>) thread safe. I only want the thread that created the stack or any of its children to have access to the Stack<string> and be able to Push() or Pop() items from it. I was wondering if ThreadAffine would be able to do this for me. I saw that when a different thread accesses instances of this type, a ThreadMismatchException exception is thrown, however in my case I don't want an exception to be thrown I would like for the Stack<string> to be left alone.

What I'm trying to do is keep track of method calls by placing the name of the methods inside the Stack<string>, but I'm running into issues under multi-threaded scenarios. Here's what I have:

public sealed class MethodNameAspect : OnMethodBoundaryAspect
{
       private static readonly AsyncLocal<Stack<string>> StackOfMethodNames = new AsyncLocal<Stack<string>>();

       private string methodName;

       public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
       {
             methodName = method.Name;
             base.CompileTimeInitialize(method, aspectInfo);
       }
   
       public override void OnEntry(MethodExecutionArgs args)
       {
            if (StackOfMethodNames.Value == null)
            {
                StackOfMethodNames.Value = new Stack<string>();
            }
            StackOfMethodNames.Value.Push(methodName);
            base.OnEntry(args);
       }

        public override void OnExit(MethodExecutionArgs args)
        {
            StackOfMethodNames.Value.Pop();
            base.OnExit(args);
        }

}

Currently this will fail if I'm in a multi-threaded environment because Stack<string> is not thread safe. If I use ImmutableStack<string> everything works fine BUT there's a huge performance hit. I was wondering if ThreadAffine would help me here (however I don't want an exception thrown, would rather it just prevent access), or is there anything else that could help me with making Stack<string> thread safe?

Thank You.

  1. Support Staff 1 Posted by PostSharp Techn... on 20 Sep, 2017 04:26 PM

    PostSharp Technologies's Avatar

    Hello,

    I would say that AsyncLocal<T> and ThreadAffine cannot coexist in one class. In the context of async functions, you never know on which managed thread the code would be running. So far we do not have a threading model that would prohibit access from other workflows than the one that created it (you also have the problem of branching, i.e. more child tasks running in parallel).

    The nature of how AsyncLocal is passed along the workflow boundaries makes use of ImmutableStack a correct solution because you don't care about the value being "branched" on multiple children. However it does allocation on every method call, which is quite a lot.

    This problem is quite specific, so it would be possible to actually optimize ImmutableStack - most of the time there is no branching and you are safe with behaving in a non-immutable way (i.e. not allocating memory). If I remember correctly, ImmutableStack is implemented internally as a tree - for each operation you have a node. For this you would need a specialized data structure.

    To wrap it up - the problem is not thread-safety, it is the branching. You could enforce thread-safety using a simple spin-lock around the Stack<T>.

    Please let me know if I did not understand the problem correctly or if you have any other questions.

    Best regards,
    Daniel

  2. 2 Posted by ON on 20 Sep, 2017 05:49 PM

    ON's Avatar

    Hi Daniel,

    Thanks for your response. You did understand my problem and provided some great information. I'm concerned with the Spin-lock because of the performance hit since it can be worse than lock sometimes.

    I'm not quite sure what you mean when you mention that it would be possible to actually optimize ImmutableStack , could you expand on that please? I was hoping that I could use ThreadAffine in order to use Stack and improve the performance but since that is not an option if I could optimize ImmutableStack<string> that could solve my issue.

    Thank You

  3. Support Staff 3 Posted by PostSharp Techn... on 21 Sep, 2017 10:08 AM

    PostSharp Technologies's Avatar

    Hello,

    to be clear the SpinLock would help you synchronizing, but it's not an option here. I agree that using it is a bit tricky.

    ImmutableStack is implemented as a tree with each instance of ImmutableStack pointing to a parent instance. This means you have one allocation per method call. In CLR this is quite cheap but would eventually increase need for GC. It's also possible that setting the async local is actually expensive.

    I have observed approximately threefold increase in execution time when using your aspect with ImmutableStacks, compared to no aspect at all. That's not that bad. However most of that overhead (~80%) seems to be changing the value of the AsyncLocal, while allocation is a smaller part. When the value is not changed the cost is significantly less.

    That means that the key is to prevent unnecessary sets to the async local. I would expect that it would be possible do this by a combination of the following:
    1) Using thread static when possible (however you would need to intercept e.g. Task.Run to know when you need to dump thread static state).
    2) Different logic for async and non-async methods (MethodLevelAspect with two sets of advices).
    3) Value change callback on AsyncLocal which is called whenever the ExecutionContext is changing.
    4) Using a stack variable to track relation between Entry and Exit (it may simplify things).

    Please let me know if you have any further questions.

  4. Support Staff 4 Posted by PostSharp Techn... on 09 Oct, 2017 12:03 PM

    PostSharp Technologies's Avatar

    Hello,

    We are going to close this request as there have not been any further updates. Please feel free to reopen the discussion if you need more help.

    Thanks,
    PostSharp Team

  5. PostSharp Technologies closed this discussion on 09 Oct, 2017 12:03 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac