c# Property和PropertyChanged线程失败
本文关键字:线程 失败 PropertyChanged Property | 更新日期: 2023-09-27 18:02:41
最后,我收到的线程无法访问,因为它由另一个线程拥有。然而,这与UI(直接)无关。只有当我试图设置对象的属性时才会抛出异常,而不是设置成员本身。如果我直接设置成员,它工作得很好,但我失去了PropertyChanged事件。请,如果我把这复杂化了,让我知道!
你可以快进到下面的第3类查看结果。在最高级别上,我有三个类:MainWindow、OCXComms和ZServer。当监听按钮被按下时,它调用ocxcomm中的启动方法,并负责创建ZServer实例,如下所示:
类# 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在"主线程"上构造ZServer对象。
类# 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是在"主线程"上创建的,此时我在侦听器线程上。我毫不怀疑这就是问题所在。然而,我不确定如何继续。我的大部分研究都指向Dispatcher,但我不在它的主窗口...."我是三个等级深;)"我试过锁,监控,新线程,都没有成功。任何建议将非常感激!
类# 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;
}
}
}
这里的问题很简单,您在一个线程上创建表单,使该线程成为该表单的UI线程。但是,在服务器中,您启动一个新线程(它不拥有该表单),当客户端连接时引发一个事件,并通过处理程序该事件被传播到comms_IsClientConnectedChanged
。但是,该传播仍然在该工作线程上,而不是在UI线程上。
因此,当该处理程序代码调用connectionStatusLbl.Content
时,它通过非UI所属线程调用UI控件。这是不允许的。
在comms_IsClientConnectedChanged
中使用.InvokeRequired
和.Invoke()
来访问form或它的任何后代控件。