-2

I have a static class that gets me a client..

public static ClientFactory {

    private static Lazy<IClient> _MyClient;

    public static IClient GetClient(ICache cache) {

        if (_MyClient == null) {
            _MyClient = new Lazy<IClient>(() => CreateClient(cache));
        }

        return _MyClient.Value;
    }

    private static IClient CreateClient(ICache cache) {
        // code that takes 1-2 seconds to complete
        return new Client(cache);
    }
}

Is there any chance that I can have 2 or more clients created by writing code like this? Where the second client would overwrite the first one?

How should I update my code in a way, such that the constructor is called only once per application?

Thanks.

3

2 Answers 2

2

Yes, your code can result in multiple Client objects created. Use a static constructor to create the Lazy<IClient> instance:

public static ClientFactory
{
    ...

    static ClientFactory()
    {
        _myClient = new Lazy<IClient>(() => CreateClient());
    }

    public IClient GetClient() => _myClient.Value;
}

Example execution from your code:

  1. thread A evaluates _myClient == null as true, enters block
  2. thread B evaluates _myClient == null as true, enters block
  3. thread A creates Lazy<IClient> object and sets _myClient field
  4. thread A exits if block
  5. before thread B sets _myClient field with a new Lazy<IClient> object, thread A accesses _myCilent.Value property, resulting in the creation of your Client object
  6. thread B sets the _myClient field to its new Lazy<IClient> object
  7. thread B accesses the _myClient.Value property, creating a second Client object
  8. all subsequent calls to GetClient will return this second instance of Client
Sign up to request clarification or add additional context in comments.

6 Comments

Why use a static constructor? Why not just assign on the same line as the declaration? private static Lazy<object> _MyClient = new Lazy<object>(() => CreateClient());
so, in this case, if many calls are made to do _MyClient.Value am I guaranteed to have created only 1 instance?
yes as the static constructor is guaranteed to execute only once per application domain and will execute before any other method in the class can be externally called
@mjwills yes, inline initialization would produce the same results in this case
So, what should I do my GetClient call needs to have some objects passed in? How do I do that with a single static constructor?
|
1

Yes, there is a chance, consider this example, based on your input, a run of this simple program may result in initialization of one or two clients (run multiple times to see):

void Main()
{
    System.Threading.Tasks.Parallel.Invoke(
        () => ClientFactory.GetClient(1),
        () => ClientFactory.GetClient(2));  
}

// You can define other methods, fields, classes and namespaces here
public static class ClientFactory {

    private static Lazy<IClient> _MyClient;

    public static IClient GetClient(int init) {
        Console.WriteLine(_MyClient == null);
        if (_MyClient == null) {
            Console.WriteLine(init);
            _MyClient = new Lazy<IClient>(() => CreateClient());
        }       
        return _MyClient.Value;
    }

    private static IClient CreateClient() {
        // code that takes 1-2 seconds to complete
        return new Client();
    }
}

public interface IClient {

}

public class Client : IClient {
    public Client() {
        Console.WriteLine("Client constucrtor");
    }
}

Two threads may be within the if (_MyClient == null) clause in the same time, creating multiple objects, only one assigned.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.