2011-12-30 13 views
5

Lo que estoy haciendo es crear un objeto (A) que contiene una referencia a otro objeto (B). La parte de la IU de mi código contiene esos objetos (A) en una lista de vinculación que se utiliza como fuente de datos para una vista de cuadrícula de DevExpress. El controlador envía los objetos recién creados (A) a la interfaz de usuario a través de un evento. El controlador también tiene un hilo que actualiza el objeto al que se hace referencia (B). La excepción lanzada proviene de DevExpress GridView y dice "Se detectó la operación de cruce de hilos. Para suprimir esta excepción, configure DevExpress.Data.CurrencyDataController.DisableThreadingProblemsDetection = true".Usar un objeto referenciado en los hilos

Ahora no quiero suprimir esta excepción porque el código eventualmente terminará en una aplicación crítica.

Entonces, ¿cómo puedo actualizar un objeto de referencia en los hilos sin causar problemas? Aquí está el código de mi aplicación de prueba. Será esencialmente lo mismo en el programa real.

ACTUALIZACIÓN El error en la interfaz de usuario ha sido fijado por la respuesta de Nicholas Butler, pero ahora la excepción se ha trasladado a la clase Employee. He actualizado el código para reflejar los cambios.

Aquí está mi código

* interfaz de usuario *

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(); 
    } 
} 

controlador:

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; 
} 

Clase Empleado: (excepción lanzada en esta clase)

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; 
} 

interfaz del empleado:

interface IEmployee : INotifyPropertyChanged 
{ 
    string Name { get; set; } 
    double Salary { get;} 
} 

Clase de pago:

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; 
} 

aprecio enormemente cualquier ayuda.

Respuesta

2

El problema es que se EmployeeController.onNewEmployee ser despedido en un hilo no la interfaz de usuario. Utilice el patrón asíncrono basado en eventos para generar los eventos en un hilo específico (en este caso, la IU): http://msdn.microsoft.com/en-us/library/hkasytyf.aspx.

Como alternativa, puede verificar IsInvokeRequired en cada controlador de eventos y, de ser así, usar .Invocar para volver al hilo de la interfaz de usuario. Esto es más engorroso, pero en su caso puede ser más fácil o más rápido de implementar.

1

Está llamando al empList.Add(empl); incluso cuando InvokeRequired == true. Proveedores:

private void AddEmployee(IEmployee empl) 
{ 
    if (InvokeRequired) 
    { 
     this.Invoke(new AddEmployeInvoke(AddEmployee), new Object[] {empl}); 
    } 
    else 
    { 
     empList.Add(empl); //exception thrown here 
    } 
} 

También es necesario aumentar sus INotifyPropertyChanged eventos en el subproceso de interfaz de usuario, pero que no tienen un control de interfaz de usuario para llamar Invoke sucesivamente. La manera más fácil de hacer esto es para almacenar una referencia a su forma principal y que sea public static:

public partial class Form1 : Form 
{ 
    public static Control UI { get; private set; } 

    public Form1() 
    { 
     UI = this; 
    } 
} 

continuación, puede utilizar Form1.UI.InvokeRequired y Form1.UI.Invoke desde cualquier lugar de su aplicación.


yo estaba tratando de dar un paso a la vez, pero si quieres una solución más correcta, se puede pasar la interfaz de usuario SynchronizationContext al controlador y utilizar sus Post o Send métodos:

public Form1() 
{ 
    controller = new EmployeeController(SynchronizationContext.Current); 
    ... 

class EmployeeController 
{ 
    private SynchronizationContext _SynchronizationContext = null; 

    public EmployeeController(SynchronizationContext sc) 
    { 
     _SynchronizationContext = sc; 
     ... 

Luego tiene que llevarlo a sus objetos. Para generar un evento que le hace esto a continuación:

var evt = this.PropertyChanged; 
if (evt != null) sc.Send(
    new SendOrPostCallback(state => evt(this, ...EventArgs...)), 
    null); 
+0

Ahh que funcionaba pero ahora en mi clase Empleado en el método "_myPay_PropertyChanged()" this.PropertyChanged (this, new PropertyChangedEventArgs ("Salario")); está arrojando la misma excepción. ¿También necesito invocar una invocación sobre eso? – Stephen

+0

Sí, he actualizado mi respuesta. –

+0

O podría implementar el modelo asíncrono basado en eventos en su EmployeeController y no tendría que hacer nada de esto ya que EmployeeController sería responsable de realizar las llamadas en el hilo de righe. Toma un poco más de tiempo para entender, pero una solución mucho más ordenada. –

Cuestiones relacionadas