C#属性和PropertyChanged线程失败

时间:2014-05-09 19:05:51

标签: c# multithreading properties locking polymorphism

最后,我收到的线程无法访问,因为它是由另一个线程拥有的。但是,这并没有与UI(直接)相关联。当我尝试设置对象的属性而不是成员本身时,仅抛出异常。如果我直接设置成员,它工作正常,但我丢失了我的PropertyChanged事件。 如果我过度复杂,请告诉我!我想不出以其他方式将属性返回给MainWindow。

您可以快速前往下面的第3组获得结果。在最高级别,我有三个类:MainWindow,OCXComms和ZSer​​ver。当按下listen按钮时,它会调用OCXComms中的启动方法,并负责创建ZServer实例,如下所示:

CLASS#1

public partial class MainWindow : Window
{
    private OCUComms comms;

        ......

    private void roxListenBtn_Click(object sender, RoutedEventArgs e)
    {
        //Debugger Shows Main Thread
        comms.StartOutput();
    }

   void comms_IsClientConnectedChanged(object sender, PropertyChangedEventArgs e)
    {
        connectionStatusLbl.Content = "Connected - No Control";
        connectionStatusLbl.Foreground = myWarningBrush;

        roverControlBtn.IsEnabled = true;
        manualControlRB.IsEnabled = true;
    }

        ......
}

接下来,通过StartOutput在“MainThread”上构建ZServer对象。这很好。

CLASS#2

public sealed class OCXComms : INotifyPropertyChanged
{

        private static OCXComms instance;
        private static readonly object instanceSync = new object();

        private Boolean _isClientConnected = false;
        private bool isEnabled;

          .....

     public static OCXComms Instance
    {
        get
        {
            if (instance == null)
            {
                lock (instanceSync)
                {
                    if (instance == null)
                        instance = new OCXComms();
                }
            }

            return instance;
        }
    }

    private OCXComms()
    {
        isEnabled = false;
        _isControllerEnabled = false;
        _isKeyboardEnabled = false;

        worker = new System.Timers.Timer();

        worker.Elapsed += new ElapsedEventHandler(worker_Elapsed);

        Properties.Settings.Default.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Default_PropertyChanged);
    }

     public event PropertyChangedEventHandler PropertyChanged;


    private void OnClientConnectedChanged()
    {
        if (PropertyChanged != null)

            PropertyChanged(this, new PropertyChangedEventArgs("IsClientConnected"));
    }

    void server_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        this.IsClientConnected = server.getIsClientConnected();
    } 

        public void StartOutput()
    {
            if (isEnabled)
                return;

            Console.WriteLine("ZServer started on: " + Thread.CurrentThread.Name);

            //Debugger Shows Main Thread
            this.server = new ZServer(Properties.Settings.Default.ListeningPort);
            this.server.PropertyChanged += server_PropertyChanged;
            this.server.Start();

            IsEnabled = true;

    }
}

这是问题发生的地方。请注意两种不同的尝试(仅在此处显示,实际上不在语法中)。如果没有断点,客户端和服务器就会挂起,如果设置了断点,则第一个更改异常会通知我线程正在尝试访问另一个线程拥有的信息。我有点清楚,ZServer是在“MainThread”上创建的,此时我在侦听器线程上。我毫不怀疑这是问题所在。但是,我不确定如何继续。我的大多数研究都导致Dispatcher,但我不是它的主窗口......“我是三类深度;”我试过锁定,监控,新线程,都没有成功。任何建议将不胜感激!

CLASS#3

public class ZServer
{
    private Thread listenThread;
    private Boolean _isClientConnected = false;

    public ZServer(String sIP, int iPort)
    {
        InitializeServer(sIP, iPort);
    }

       private void InitializeServer(String sIP, int iPort)
    {
        this.tcpListener = new TcpListener(IPAddress.Parse(sIP), iPort);
        this.listenThread = new Thread(new ThreadStart(ListenForClients_DoWork));
        this.listenThread.Name = "Listen Thread";

    }

            public Boolean IsClientConnected
    {
        get 
        {
        return _isClientConnected;
        }

        private set 
        {
            _isClientConnected = value;
            OnClientConnectedChanged();
        }
    }

    private void OnClientConnectedChanged()
    {
        if (PropertyChanged != null)

            PropertyChanged(this, new PropertyChangedEventArgs("IsClientConnected"));
    }


     private void ListenForClients_DoWork()
    {
        // Start the Server Listening for Clients to connect while blocking.
        this.tcpListener.Start();

        while (!tokenSource.Token.IsCancellationRequested)
        {
            try
            {

                TcpClient client = this.tcpListener.AcceptTcpClient();

                this._isClientConnected = true;  //<== This Works
                this.IsClientConnected = true; //This causes client and server to hang. Possible deadlock?
                ........
            }
            catch (SocketException socketEx)
            {
                Console.WriteLine("Socket Closed from Another Thread: " + socketEx.ToString());
            }
            catch (Exception ex)
            {
                Console.WriteLine("ZServer: unhandled exception in ListenForClients: " + ex.Message);
            }
        }
        Console.WriteLine("ListenForClients exiting...");
    }

    public Boolean getIsClientConnected()
    {
        lock (_clientConnectedLock)
        {
            return _isClientConnected;
        }
    }   

}

1 个答案:

答案 0 :(得分:0)

这里的问题很简单,您在一个线程上创建一个表单,使该线程成为该表单的UI线程。但是,在服务器中,您启动一​​个新线程(不拥有该表单),当客户端连接时会引发一个事件,并通过处理程序将该事件传播到comms_IsClientConnectedChanged。但是,该传播仍然在该工作线程上,而不是UI线程。

因此,当该处理程序代码调用connectionStatusLbl.Content时,它会通过非UI拥有的线程调用UI控件。这是禁忌。

访问表单或其任何后代控件时,在.InvokeRequired内使用.Invoke()comms_IsClientConnectedChanged。这将解决您的问题。