Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this the proper way to implement a thread-safe read/write Guid property?

I am implementing a class that will be used concurrently from multiple threads. Most of the properties get and set primitive types which can be handled properly by the Interlocked class. The class includes a Guid property. This is not as straight-forward to implement in a thread-safe manner. Is this how you would implement the property? Thanks in advance.

private Byte[] _activityId;
public Guid ActivityId 
    {
        get { return new Guid(this._activityId); }
        set
        {
            Byte[] bytes = value.ToByteArray();
            Interlocked.Exchange(ref this._activityId, bytes);
        }
    }

UPDATE: So the only proposed solution up to this point doesn't include the use of any "Threading" classes or constructs. So I am going to pose the question that I've already posed in comments:

My understanding is that reference/primitive values types assignments are atomic however Interlocked will guarantee the change is propagated to all threads. If we could simply just assign the value, why does Interlocked expose APIs to exchange reference types and primitive values?

like image 603
Josh Avatar asked Dec 29 '11 16:12

Josh


2 Answers

You can get cheaper atomic assignment by creating your own box class:

class Box<T> where T : struct {
    public readonly T Value;
    public Box(T value) { Value = value; }
}

By storing a reference to the (immutable) Box instance instead of storing the value directly, all operations on the field will be atomic.

private Box<Guid> _activityId;
public Guid ActivityId {
    get { return this._activityId.Value; }
    set { this._activityId = new Box<Guid>(value); }
}

This way, the non-atomic struct copy operations happen in new Box<Guid>(value) and in the .Value access. Since they don't involve the field, they won't cause trouble.

This should be much faster than using byte arrays, and a little bit faster than native boxing with a cast. (disclaimer: I haven't measured)

like image 120
SLaks Avatar answered Oct 14 '22 13:10

SLaks


I think you could use the other overload of Interlocked.Exchange:

private volatile object _activityId; // Yes, object :)
public Guid ActivityId {
    get { return (Guid)_activityId; }
    set { _activityId = value; }
}

This works because the Guid is now boxed, and the assignment of reference types is atomic.

like image 43
Sergey Kalinichenko Avatar answered Oct 14 '22 12:10

Sergey Kalinichenko