新线程无法识别已创建单线程的主线程

时间:2014-08-19 17:06:19

标签: c# multithreading singleton

我不确定为什么我的新线程无法识别已经创建的单例实例。在启动时,我有一个Repository类,它创建一个COM_Component类,它创建一个DataSubscriber类。这是实例化的顺序:

  1. 创建singleton Repository类。
  2. Repository类创建COM_Component类。
  3. COM_Component类创建DataSubscriber类。
  4. COM_Component方法为DataSubscriber生成新线程以侦听传入数据。
  5. 新线程上的DataSubscriber接收数据并使用Repository.Instance()来存储数据。
  6. 问题是,当DataSubscriber调用单例时,它不会识别它之前被调用并调用构造函数,后者继续循环遍历上面的所有步骤。我认为我有单例设置,以便多个线程可以正确访问单例。我意识到删除多线程会更好,但这是设置示例的方式,我想快速启动并运行。

    以下是Repository类的外观:

    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;
            }
        }
    

    COM_Component创建DataSubscriber并启动监听线程:

       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++;
       }
    

    然后DataSubscriber的数据处理程序是OnDataReceived(),在新线程上运行。这是对Repository.Instance的调用再次触发构造函数:

        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);
            }
        }
    

    我很欣赏有关如何修改此代码并使其快速运行的提示,因为我已经阅读了有关多线程龙的帖子,而且我对番茄酱很脆弱。 :)

    谢谢! Myca

1 个答案:

答案 0 :(得分:3)

我认为你有一个简单的竞争条件:

1)第一次调用Repository.Instance会调用Repository构造函数

2)Repository构造函数通过component.StartListen()

启动线程

3)其中一个线程进入OnDataReceived()并在构造函数返回原始线程之前调用Repository.Instance

4)_instance此时仍然为空,因为在构造函数返回之前不会发生赋值,因此代码会创建另一个实例

也许移动这条指令:

component.StartListen();

Instance getter本身:

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

请注意,这意味着如果有人在正确的时刻拨打电话,则可以在设置完成之前获得_instance。您必须确定这是否真的可能,如果是,那么它是否是一个问题。