从工作线程更新视图模型会引发跨线程问题
Updating viewmodel from worker thread throws cross thread issue
最近,我一直在测试实现 INotifyPropertyChanged
的类型绑定,并从抛出跨线程问题的工作线程更新 属性。
这是示例代码:
public class MyViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler hanlder = PropertyChanged;
if(hanlder != null)
hanlder(this, new PropertyChangedEventArgs(propertyName));
}
}
以上视图模型已与 windows 形式的标签文本绑定,并从工作线程更新标签值。
从工作线程更新 label1 文本导致跨线程问题:
public partial class MainForm : Form
{
private MyViewModel _myViewModel = new MyViewModel();
public MainForm()
{
InitializeComponent();
Btn1.Click += Btn1_Click;
label1.DataBindings.Add("Text", _myViewModel, "Name");
}
private void Btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
_myViewModel.Name = "Updating from worker thread!"; It causes cross thread issue
});
}
}
到目前为止,我可以相信这是由于从工作线程更新 UI。是否有任何解决方法可以在不更改按钮单击方法的情况下使其线程安全,即可能在 viewmodel 中使线程安全。
您尝试使用 CheckForIllegalCrossThreadCalls 了吗?
像这样:
private void Btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
CheckForIllegalCrossThreadCalls = false;
_myViewModel.Name = "Updating from worker thread!"; It causes cross thread issue
CheckForIllegalCrossThreadCalls = true;
});
}
我并不是说这是最好的方法,但当我使用线程时它对我有用
编辑:
private void Btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
_myViewModel.Invoke((MethodInvoker)(() => _myViewModel.Name = "Updating from worker thread!"; ));
});
}
获取 UI 的 SynchronizationContext
(例如,在应用程序启动时使用 UI 线程中的 SynchronizationContext.Current
),并将其存储在某个静态变量中某处(我称之为 uiSynchronizationContext
)。
然后在您的 OnPropertyChanged
上执行如下操作:
protected virtual void OnPropertyChanged(string propertyName)
{
uiSynchronizationContext.Post(
o => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName))
,null
);
}
或者你可以使用 Send
而不是 Post
如果你希望同步操作(与启动 send/post 操作的线程同步,它总是 "synchronous" 在 UI 线程上)
我特别不喜欢在多线程时进行直接数据绑定(我更喜欢在 UI 线程上使用计时器从一些图形对象进行更改),但这应该可以解决问题。
我承认我没有测试过
最近,我一直在测试实现 INotifyPropertyChanged
的类型绑定,并从抛出跨线程问题的工作线程更新 属性。
这是示例代码:
public class MyViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler hanlder = PropertyChanged;
if(hanlder != null)
hanlder(this, new PropertyChangedEventArgs(propertyName));
}
}
以上视图模型已与 windows 形式的标签文本绑定,并从工作线程更新标签值。
从工作线程更新 label1 文本导致跨线程问题:
public partial class MainForm : Form
{
private MyViewModel _myViewModel = new MyViewModel();
public MainForm()
{
InitializeComponent();
Btn1.Click += Btn1_Click;
label1.DataBindings.Add("Text", _myViewModel, "Name");
}
private void Btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
_myViewModel.Name = "Updating from worker thread!"; It causes cross thread issue
});
}
}
到目前为止,我可以相信这是由于从工作线程更新 UI。是否有任何解决方法可以在不更改按钮单击方法的情况下使其线程安全,即可能在 viewmodel 中使线程安全。
您尝试使用 CheckForIllegalCrossThreadCalls 了吗?
像这样:
private void Btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
CheckForIllegalCrossThreadCalls = false;
_myViewModel.Name = "Updating from worker thread!"; It causes cross thread issue
CheckForIllegalCrossThreadCalls = true;
});
}
我并不是说这是最好的方法,但当我使用线程时它对我有用
编辑:
private void Btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
_myViewModel.Invoke((MethodInvoker)(() => _myViewModel.Name = "Updating from worker thread!"; ));
});
}
获取 UI 的 SynchronizationContext
(例如,在应用程序启动时使用 UI 线程中的 SynchronizationContext.Current
),并将其存储在某个静态变量中某处(我称之为 uiSynchronizationContext
)。
然后在您的 OnPropertyChanged
上执行如下操作:
protected virtual void OnPropertyChanged(string propertyName)
{
uiSynchronizationContext.Post(
o => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName))
,null
);
}
或者你可以使用 Send
而不是 Post
如果你希望同步操作(与启动 send/post 操作的线程同步,它总是 "synchronous" 在 UI 线程上)
我特别不喜欢在多线程时进行直接数据绑定(我更喜欢在 UI 线程上使用计时器从一些图形对象进行更改),但这应该可以解决问题。
我承认我没有测试过