views:

106

answers:

3

Lets say we have an object that can be accessed by multiple threads and a global singleton handing out the object's reference, also accessible by multiple threads.

class Book {
    private string title;
    public Book(string title) {
        this.title = title;
    }

    public string Title { get { return title; } }
}

class BookstoreSingleton {

    public BookstoreSingleton Instance { ... }

    public Book Book { get { return this.book; } }
}

Am I correct in thinking that both, the Book.Title and BookstoreSingleton.Book both need thread-safe code?

+2  A: 

Your class seems immutable and thread safe. You don't need to synchronize access to those properties as they can only be read, just make sure that you initialize your singleton only once (the Instance property) and that you cannot assign it a value a second time.

Darin Dimitrov
Can't multiple threads have the reference to same book object and at the same time try to access that book's Title property, thus referencing the same memory simultaneously? Wouldn't this cause a problem?
randomguy
@randomguy, no, because they would be only reading this memory. Problems start to arise when multiple threads read and write the same shared memory location.
Darin Dimitrov
Do I specifically need to set them to `readonly`? I can't understand what guarantees simultaneous reads won't cause access violation because memory is currently read (and thus locked) by another thread. Does the .NET do some underlying magic to make this work? There seems something about immutability I don't understand. Would you have any resources about immutable object and thread-safety?
randomguy
@randomguy: Reading memory doesn't lock it. Actually, *nothing* implicitly locks it -- writing doesn't either. A hundred threads could be reading and writing the same memory at the same time and (without explicit locking) the CLR won't stop them. But if they're all reading the same thing, and the API prevents any of them changing it (as your Book.Title with no setter, making it a read-only property), then there's no worry about the data being "between" one valid value and another, since it'll never have to change.
cHao
@randomguy: any object that can't be changed is by definition immutable. Since your Book's Title property can't be set, and there are no other properties to worry about and no other functions that change the title to something else, the object can't be modified once it's created.
cHao
@randomguy: why do you believe that simultaneous *anything* causes an *access violation*? Access violations are caused by reading pages that are marked as unreadable, not by two threads reading the same readable page. And why do you believe that reading "locks" memory? You seem to have a bunch of ideas about how memory access works that are nothing at all like how memory access actually works on any chipset I'm familiar with. Can you explain the origins of these beliefs?
Eric Lippert
@Eric Lippert, it's probably because I don't understand, among many other things, how memory works. :) I imagine memory reads, at the lowest level, as electrical currents passing through logical gates returning values. I don't see how that electric process could be executed "multi-threaded". I think my logical fallacy begins there. Based on that, it would mean that hardware/software at some level controls synchronization (ie. queuing reads) to that critical section (block of memory). But whether it is up to the programmer or hardware/OS/framework/CLR to handle that is the question.
randomguy
Is it absurd to question or not take the leap of faith in believing that the underlying system (whether hardware or software) does *not* handle this synchronization? Why? On the side note, I now know what CS course I'm taking next year. :)
randomguy
I made an follow-up question: http://stackoverflow.com/questions/3595114
randomguy
Sure, that's how memory works but I assure you it is *way* more complicated than that. Start by reading up on how modern processors store recently used memory blocks on the (several) processor caches if you want to understand how this works at a low level.
Eric Lippert
Also, it will help greatly to have a correct understanding of *single processor* multithreading vs *multiple processor* multithreading. In single proc multithreading, two threads don't run at the same time. One is suspended, its state is saved, and then a different thread takes over the processor. But with two processors there actually can be two threads running *at the same time*. Since processors make *copies* of memory in their caches that means that each processor can have *inconsistent* views of memory. It gets real complicated real fast.
Eric Lippert
+2  A: 

The only thing that really needs synchronization is the Instance function in the singleton class, and only then if you create your instance lazily. The rest should be fine, since the book is immutable.

cHao
@cHao, no he doesn't need to synchronize the access to `Instance` as if this is a real singleton it should be initialized only once and so it is also immutable during the lifetime of the application.
Darin Dimitrov
@Darin: If the initialization is done lazily, as is quite common, then the `Instance` method creates the object if it's not already created. Two threads therefore need to be prevented from running `Instance` at the exact same time, or two singletons could be created.
cHao
@cHao, that's correct, but if the singleton pattern is implemented correctly (http://www.yoda.arachsys.com/csharp/singleton.html), this cannot happen. Two threads could read the Instance property at the same time (which is not problematic), but only a single one will assign it (and this before any others had the chance to read it).
Darin Dimitrov
@Darin: I'd argue that that page's version of "correctly" is language/platform trickery. The Singleton pattern doesn't require (or even hint at) inner classes and all that, and they work because of how the CLR does static initialization -- which people who want to get stuff done shouldn't have to know in depth just to make a singleton. The most straightforward way to initialize lazily yet with thread safety is the second one in that page. It's not the best performing, but then, i also don't have to worry about how to translate it to, say, PHP, C++ or Java. And it's easy to explain.
cHao
cHao, totally agree with you. The second example is thread safe and if you are not worried about performance perfectly valid and understandable. But usually when you pick a platform/framework is because it offers benefits that others might not, thus the fifth version.
Darin Dimitrov
+1  A: 

I agree with cHao.

The classic way to do it is to include

public class BookstoreSingleton {

    private static readonly BookstoreSingleton instance = new BookstoreSingleton()
    public BookstoreSingleton Instance { return instance; }

    public Book Book { get { return this.book; } }
}

as member of the BookstoreSingleton class. Not a lazy as we would be if Instance is the only static member, it will do the job perfectly and is perfectly thread safe as specified by the c# specs (static initializer are executed only once). The readonly ensure that this is the only time the instance member will be set.

That's for the singleton part but now where do you set the BookstoreSingleton's Book member. You probably need thread safety there except if you put it in the private BookstoreSingleton constructor only called once by the static initializer.

VdesmedT