跨线程使用引用对象
本文关键字:引用 对象 线程 | 更新日期: 2023-09-27 18:19:10
我正在做的是创建一个对象(A),该对象(A)持有对另一个对象(B)的引用。我的代码的UI部分在BindingList中保存这些对象(A),该BindingList用作DevExpress网格视图的数据源。控制器通过事件将新创建的对象(A)发送给UI。控制器也有一个线程更新被引用的对象(B)。抛出的异常来自DevExpress GridView并读取"检测到交叉线程操作"。要抑制此异常,设置devexpress . data . currencydataconcontroller . disablethreadingproblemsdetection = true"。
现在我不想抑制这个异常,因为代码最终会在一个关键的应用程序中结束。
那么我如何跨线程更新引用对象而不引起问题呢?这是我的测试应用程序的代码。在实际的程序中是基本相同的。
UI中的错误由Nicholas Butler的回答修复,但现在异常已经移动到Employee类中。我已经更新了代码以反映这些变化。
我的代码
UI * *
public partial class Form1 : Form
{
private BindingList<IEmployee> empList;
EmployeeController controller;
private delegate void AddEmployeInvoke(IEmployee employee);
public Form1()
{
controller = new EmployeeController();
controller.onNewEmployee += new EmployeeController.NewEmployee(controller_onNewEmployee);
empList = new BindingList<IEmployee>();
InitializeComponent();
}
void controller_onNewEmployee(IEmployee emp)
{
AddEmployee(emp);
}
private void AddEmployee(IEmployee empl)
{
if (InvokeRequired)
{
this.Invoke(new AddEmployeInvoke(AddEmployee), new Object[] {empl});
}
else
{
empList.Add(empl);
}
}
private void Form1_Load(object sender, EventArgs e)
{
this.gridControl1.DataSource = empList;
this.gridControl1.RefreshDataSource();
controller.Start();
}
}
控制器:
class EmployeeController
{
List<IEmployee> emps;
Task empUpdater;
CancellationToken cancelToken;
CancellationTokenSource tokenSource;
Pay payScale1;
Pay payScale2;
public EmployeeController()
{
payScale1 = new Pay(12.00, 10.00);
payScale2 = new Pay(14.00, 11.00);
emps = new List<IEmployee>();
}
public void Start()
{
empUpdater = new Task(AddEmployee, cancelToken);
tokenSource = new CancellationTokenSource();
cancelToken = tokenSource.Token;
empUpdater.Start();
}
public bool Stop()
{
tokenSource.Cancel();
while (!empUpdater.IsCompleted)
{ }
return true;
}
private void AddEmployee()
{
IEmployee emp = new Employee("steve", ref payScale1);
ThrowEmployeeEvent(emp);
emps.Add(emp);
emp = new Employee("bob", ref payScale2);
ThrowEmployeeEvent(emp);
emps.Add(emp);
int x = 0;
while (!cancelToken.IsCancellationRequested)
{
emp = new Employee("Emp" + x, ref payScale1);
ThrowEmployeeEvent(emp);
x++;
emp = new Employee("Emp" + x, ref payScale2);
ThrowEmployeeEvent(emp);
Thread.Sleep(1000);
payScale2.UpdatePay(10.0);
payScale1.UpdatePay(11.0);
Thread.Sleep(5000);
}
}
private void ThrowEmployeeEvent(IEmployee emp)
{
if (onNewEmployee != null)
onNewEmployee(emp);
}
public delegate void NewEmployee(IEmployee emp);
public event NewEmployee onNewEmployee;
}
Employee类:(该类抛出异常)
class Employee : IEmployee
{
private string _name;
private double _salary;
private Pay _myPay;
public string Name
{
get { return _name; }
set { _name = value;
//if (PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
public double Salary
{
get { return _salary; }
}
int x = 1;
public Employee(string name, ref Pay pay)
{
_myPay = pay;
_myPay.PropertyChanged += new PropertyChangedEventHandler(_myPay_PropertyChanged);
_salary = _myPay.Salary;
Name = name;
}
void _myPay_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Salary")
{
_salary = _myPay.Salary;
if (this.PropertyChanged != null)
// exception thrown on the line below
this.PropertyChanged(this, new PropertyChangedEventArgs("Salary"));
}
}
public void ChangeName()
{
Name = "Me " + x;
x++;
}
public event PropertyChangedEventHandler PropertyChanged;
}
员工接口: interface IEmployee : INotifyPropertyChanged
{
string Name { get; set; }
double Salary { get;}
}
支付类:
class Pay : INotifyPropertyChanged
{
private double _salary;
private double _bonus;
public double Salary { get { return _salary; } set { _salary = value; if(PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("Salary"));} }
public double Bonus { get { return _bonus; } set { _bonus = value; if (PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("Bonus")); } }
public Pay(double salary, double bonus)
{
Salary = salary;
Bonus = bonus;
}
public void UpdatePay(double salary)
{
Salary += salary;
if (onChange != null)
this.onChange();
}
public void UpdatePay(double salary, double bonus)
{
Salary += salary;
Bonus += bonus;
if (onChange != null)
this.onChange();
}
public delegate void Change();
public event Change onChange;
public event PropertyChangedEventHandler PropertyChanged;
}
我非常感谢任何帮助。
问题是EmployeeController.onNewEmployee
在非UI线程上被触发。使用基于事件的异步模式在特定线程(在本例中是UI)上引发事件:http://msdn.microsoft.com/en-us/library/hkasytyf.aspx.
或者你可以在每个事件处理程序中检查isinvokerrequirequired,如果是的话,使用.Invoke返回UI线程。
您正在呼叫empList.Add(empl);
,即使InvokeRequired == true
。试一试:
private void AddEmployee(IEmployee empl)
{
if (InvokeRequired)
{
this.Invoke(new AddEmployeInvoke(AddEmployee), new Object[] {empl});
}
else
{
empList.Add(empl); //exception thrown here
}
}
您还需要在UI线程上提高您的INotifyPropertyChanged
事件,但是您没有一个UI控件来调用Invoke
。要做到这一点,最简单的方法是存储对主表单的引用,并将其设置为public static
:
public partial class Form1 : Form
{
public static Control UI { get; private set; }
public Form1()
{
UI = this;
}
}
你可以在你的应用程序的任何地方使用Form1.UI.InvokeRequired
和Form1.UI.Invoke
。
我试图一步一步,但如果你想要一个更正确的解决方案,你可以将UI SynchronizationContext
传递给控制器,并使用其Post
或Send
方法:
public Form1()
{
controller = new EmployeeController( SynchronizationContext.Current );
...
class EmployeeController
{
private SynchronizationContext _SynchronizationContext = null;
public EmployeeController( SynchronizationContext sc )
{
_SynchronizationContext = sc;
...
然后你必须把它传递给你的对象。要引发事件,您可以这样做:
var evt = this.PropertyChanged;
if ( evt != null ) sc.Send(
new SendOrPostCallback( state => evt( this, ...EventArgs... ) ),
null );