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:
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.