简体   繁体   中英

new thread doesn't recognize main thread already created singleton

I am unsure why my new thread doesn't recognize the singleton instance that was already created. At startup, I have a Repository class that creates a COM_Component class, which creates a DataSubscriber class. Here is the order of instantiation:

  1. Create singleton Repository class.
  2. Repository class creates COM_Component class.
  3. COM_Component class creates DataSubscriber class.
  4. COM_Component method spawns new thread for DataSubscriber to listen for incoming data.
  5. DataSubscriber on new thread receives data and uses Repository.Instance() to store data.

The problem is, when DataSubscriber calls the singleton, it does not recognize it being called before and calls the constructor, which continues to loop through all the steps above repeatedly. I thought that I had the singleton setup so that multiple threads could access the singleton correctly. I realize it would be better to remove the multi-threading, but this is the way the example was setup and I'd like to get something up and running quickly.

Here's how the Repository class looks:

public class Repository
{
    public COM_Component component;
    public String defaultProjectName = "MainDB";
    public DataSet projectRepo;
    public DataTable theProjects;
    public DataTable theTasks;

    private static Repository _instance = null;
    private static readonly object _locker = new object();

    private Repository()
    {
        InitializeRepos();
        lock (_locker)
        {
            component = new COM_Component();
            component.StartListen();
        }
    }

    public static Repository Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_locker)
                {
                    if (_instance == null)
                    {
                        _instance = new Repository();
                    }
                }
            }
            return _instance;
        }
    }

The COM_Component creates the DataSubscriber and starts the listening thread:

   public COM_Component()
   {
   }

   public void StartListen()
   {
       dataSubscriber = new DataSubscriber(this);

       //Spawn a new thread for each subscriber, condense into a single threaded subscriber in the near future
       _listenThread[_numThreads] = new Thread(new ThreadStart(DataSubscriber.Listen));
       _listenThread[_numThreads].Name = "DataSubscriber";
       _listenThread[_numThreads].Start();
       _numThreads++;
   }

And then the data handler for the DataSubscriber is OnDataReceived(), operating on the new thread. It's the calls to Repository.Instance that trigger the constructor again:

    public void OnDataReceived(DataType msg)
    {
        var selectStatement = string.Format("TaskName = '{0}'", new string(msg.msgID.Value));
        DataRow[] rows = Repository.Instance.theTasks.Select(selectStatement);
        if (rows.Length < 1)
        {
            DataRow newRow = Repository.Instance.theTasks.NewRow();
            Guid thisGuid = new Guid(); 
            newRow["TaskGuid"] = thisGuid;
            newRow["PlanID"] = Repository.Instance.defaultProjectName;
            newRow["TaskName"] = new string(msg.msgID.Value);      
            Repository.Instance.theTasks.Rows.Add(newRow);
        }
    }

I'd appreciate tips on how to modify this code and get it working quickly, as I have already read the posts about multi-threaded dragons and that I am crunchy and good with ketchup. :)

Thanks! Myca

I think you have a simple race condition:

1) The first call to Repository.Instance calls the Repository constructor

2) The Repository constructor kicks off threads by way of component.StartListen()

3) One of those threads gets into OnDataReceived() and calls Repository.Instance before the constructor has returned on the original thread

4) _instance is still null at this point since the assignment does not occur until after the constructor returns, so the code creates another instance

Perhaps move this instruction:

component.StartListen();

to the Instance getter itself:

if (_instance == null)
{
   _instance = new Repository();
   _instance.component.StartListen();
}

Note that this means someone could get _instance before listening is done being set up if they call it at the exact right moment. You'd have to decide whether that's actually possible and, if so, whether it's a problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM