una traves todos thread subprocesos subproceso seguras requiere realizar que para otro metodo make los llamó llamar llamadas interfaz how función formularios evaluación ejecuten diferente desde cross controles control como checkforillegalcrossthreadcalls calls aplicación aplanó acceder winforms invoke ui-thread invokerequired

winforms - traves - Ejecutar código en el subproceso de la interfaz de usuario sin objeto de control presente



metodo invoke c# (5)

Actualmente estoy tratando de escribir un componente donde algunas partes de él deberían ejecutarse en el hilo de la interfaz de usuario (la explicación sería demasiado larga). Entonces, la forma más fácil sería pasarle un control y usar InvokeRequired / Invoke en él. Pero no creo que sea un buen diseño pasar una referencia de control a un componente de "datos / fondo", entonces estoy buscando una forma de ejecutar código en el hilo de UI sin la necesidad de tener un control disponible . Algo así como Application.Dispatcher.Invoke en WPF ...

cualquier idea, señor Martin


Tienes razón, no es bueno pasar controles a los hilos. Los controles de Winforms son de un solo subproceso, pasarlos a varios subprocesos puede causar condiciones de carrera o romper su UI. En su lugar, debe hacer que las funciones de su subproceso estén disponibles para la interfaz de usuario y dejar que llame al subproceso cuando la interfaz de usuario esté lista y lista. Si desea que los hilos de fondo desencadenen cambios en la UI, exponga un evento de fondo y suscríbase desde la interfaz de usuario. El hilo puede disparar eventos cuando quiera y la IU puede responder cuando sea capaz.

Crear esta comunicación bidireccional entre hilos que no bloquea el hilo de UI es mucho trabajo. Aquí hay un ejemplo altamente abreviado usando una clase BackgroundWorker:

public class MyBackgroundThread : BackgroundWorker { public event EventHandler<ClassToPassToUI> IWantTheUIToDoSomething; public MyStatus TheUIWantsToKnowThis { get { whatever... } } public void TheUIWantsMeToDoSomething() { // Do something... } protected override void OnDoWork(DoWorkEventArgs e) { // This is called when the thread is started while (!CancellationPending) { // The UI will set IWantTheUIToDoSomething when it is ready to do things. if ((IWantTheUIToDoSomething != null) && IHaveUIData()) IWantTheUIToDoSomething( this, new ClassToPassToUI(uiData) ); } } } public partial class MyUIClass : Form { MyBackgroundThread backgroundThread; delegate void ChangeUICallback(object sender, ClassToPassToUI uiData); ... public MyUIClass { backgroundThread = new MyBackgroundThread(); // Do this when you''re ready for requests from background threads: backgroundThread.IWantTheUIToDoSomething += new EventHandler<ClassToPassToUI>(SomeoneWantsToChangeTheUI); // This will run MyBackgroundThread.OnDoWork in a background thread: backgroundThread.RunWorkerAsync(); } private void UserClickedAButtonOrSomething(object sender, EventArgs e) { // Really this should be done in the background thread, // it is here as an example of calling a background task from the UI. if (backgroundThread.TheUIWantsToKnowThis == MyStatus.ThreadIsInAStateToHandleUserRequests) backgroundThread.TheUIWantsMeToDoSomething(); // The UI can change the UI as well, this will not need marshalling. SomeoneWantsToChangeTheUI( this, new ClassToPassToUI(localData) ); } void SomeoneWantsToChangeTheUI(object sender, ClassToPassToUI uiData) { if (InvokeRequired) { // A background thread wants to change the UI. if (iAmInAStateWhereTheUICanBeChanged) { var callback = new ChangeUICallback(SomeoneWantsToChangeTheUI); Invoke(callback, new object[] { sender, uiData }); } } else { // This is on the UI thread, either because it was called from the UI or was marshalled. ChangeTheUI(uiData) } } }


Coloque la manipulación de la interfaz de usuario en un método en el formulario que se va a manipular y pase un delegado al código que se ejecuta en el hilo de fondo, a la APM. No tiene que usar params object p , puede escribirlo fuertemente para sus propios fines. Esta es solo una muestra genérica simple.

delegate UiSafeCall(delegate d, params object p); void SomeUiSafeCall(delegate d, params object p) { if (InvokeRequired) BeginInvoke(d,p); else { //do stuff to UI } }

Este enfoque se basa en el hecho de que un delegado se refiere a un método en una instancia particular; al hacer que la implementación sea un método de la forma, usted pone el formulario en el alcance como this . Lo siguiente es semánticamente idéntico.

delegate UiSafeCall(delegate d, params object p); void SomeUiSafeCall(delegate d, params object p) { if (this.InvokeRequired) this.BeginInvoke(d,p); else { //do stuff to UI } }


¿Qué tal pasar un System.ComponentModel.ISynchronizeInvoke? De esa forma puedes evitar pasar un Control.


En primer lugar, en su constructor de formulario, mantenga una referencia de ámbito de clase al objeto SynchronizationContext.Current (que de hecho es un WindowsFormsSynchronizationContext ).

public partial class MyForm : Form { private SynchronizationContext syncContext; public MyForm() { this.syncContext = SynchronizationContext.Current; } }

Luego, en cualquier lugar dentro de su clase, use este contexto para enviar mensajes a la IU:

public partial class MyForm : Form { public void DoStuff() { ThreadPool.QueueUserWorkItem(_ => { // worker thread starts // invoke UI from here this.syncContext.Send(() => this.myButton.Text = "Updated from worker thread"); // continue background work this.syncContext.Send(() => { this.myText1.Text = "Updated from worker thread"; this.myText2.Text = "Updated from worker thread"; }); // continue background work }); } }

Necesitará los siguientes métodos de extensión para trabajar con expresiones lambda: http://codepaste.net/zje4k6


Hay una manera mejor y más abstracta de hacer esto que funciona tanto en WinForms como en WPF:

System.Threading.SynchronizationContext.Current.Post(theMethod, state);

Esto funciona porque WindowsForms instala un objeto WindowsFormsSynchronizationContext como el contexto de sincronización actual. WPF hace algo similar, instalando su propio contexto de sincronización especializado ( DispatcherSynchronizationContext ).

.Post corresponde a control.BeginInvoke , y .Send corresponde al control.Invoke . .Send .