从工作线程更新viewmodel会引发跨线程问题

时间:2016-04-18 07:40:58

标签: c# winforms c#-4.0 task worker-thread

最近,我一直在测试使用类型的绑定来实现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));

        }
    }

以上viewmodel已与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。是否有任何工作可以使线程安全,而无需更改按钮单击方法,即可能在视图模型中使线程安全。

2 个答案:

答案 0 :(得分:5)

抓住用户界面SynchronizationContext(例如,当应用启动时,从UI线程使用SynchronizationContext.Current),并将其存储在某个地方的某个静态变量中(我已经调用过)它uiSynchronizationContext)。

然后在你的OnPropertyChanged做类似的事情:

protected virtual void OnPropertyChanged(string propertyName)
{
   uiSynchronizationContext.Post(
        o => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName))
       ,null
     );
}

如果您希望该操作同步(与启动发送/发布操作的线程同步,它始终是"同步&),您可以使用Send而不是Post #34;在UI线程上)

我特别不喜欢在多线程时进行直接数据绑定(我更喜欢使用带有更改的某个图形对象在UI线程上使用计时器进行轮询),但这应该可以解决问题。

我很欣赏它没有经过测试

答案 1 :(得分:-1)

您是否尝试使用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!"; ));
    });
}