Is it thread safe to update different properties of an object from different threads?

Viewed 1236

Consider the pseudo code below. I have a class with 3 properties and each gets populated from a different methods in parallel.

Would I get into trouble populating different properties of the same class instance from separate threads? I've setup a .net fiddle and it seems like it runs fine.

If this code would cause threading issues, what method should I use to lock the specific instance of the Response class while populating the properties?

class Response {
    public string Response1 { get; set; }
    public string Response2 { get; set; }
    public string Response3 { get; set; }
}

void foo() {
    var response = new Response();

    var task1 = Task.Run(() => GetResponse1(response));
    var task2 = Task.Run(() => GetResponse2(response));
    var task3 = Task.Run(() => GetResponse3(response));

    Task.WaitAll(task1, task2, task3);        
}

void GetResponse1(Response response) {
    response.Response1 = fetchSomeValue1();
}

void GetResponse2(Response response) {
    response.Response2 = fetchSomeValue2();
}

void GetResponse3(Response response) {
    response.Response3 = fetchSomeValue3();
}
2 Answers

Since there is only one instance of the class (held in the response local variable), then there is nothing to lock at the class level. There is an implicitly captured closure over the response variable as it passes into those tasks, but that is just a pointer to the instance sitting in heap memory - not the object itself.

Since each auto-property's backing field is independent (held in a separate memory location within the class instance), and each method is updating a separate property (and thus a separate field), then you have no update conflict. These backing fields are not interdependent.

In other words, no two tasks are trying to update the same memory location at the same time, and thus this code is thread safe.

However, if you had more than one task updating the same item at potentially the same time, then it would not be thread safe, and you'd have to implement a locking mechanism or store your data in an inherently thread-safe object (such as a ConcurrentDictionary, etc.).

I suggest you go this way:

var response = Task<Response>
                    .Factory
                    .ContinueWhenAll(new Task<string>[]{task1, task2, task3},
                                            tasks => new Response {
                                                                    Response1 = tasks[0].Result, 
                                                                    Response2 = tasks[1].Result, 
                                                                    Response3 = tasks[2].Result
                                                                    })
                    .Result;

https://dotnetfiddle.net/LHUa6G

Related