Pregunta

semántica de bloqueo impares de .Net me están molestando de nuevo.

Estoy lanzando un hilo, el hilo hijo a su vez inicia una forma. El hilo padre debe esperar hasta que se crea el formulario.

Mi primer intento fue usar un monitor para ver la variable de forma:

private void OpenForm()
{
    if (FormThread == null)
    {
        Monitor.Enter(Form);
        FormThread = new Thread(FormStub);
        FormThread.SetApartmentState(ApartmentState.STA);
        FormThread.Start();
        Monitor.Wait(Form);
        Monitor.Exit(Form);
    }
}

private void FormStub()
{
    Form = new ConnectorForm();
    Monitor.Enter(Form);
    Monitor.PulseAll(Form);
    Monitor.Exit(Form);
    Application.Run(Form);
}

... Esto arroja una excepción. Monitor.Enter () falla, ya Formulario == null.

Podría muy fácilmente crear un número entero o algo ficticio (De hecho, creo que voy a canabalize la variable FormThread), pero me preguntaba si había una solución más elegante.

¿Fue útil?

Solución

Mejor primitiva de sincronización para este caso:

private ManualResetEvent mre = new ManualResetEvent(false);

private void OpenForm()
{
    if (FormThread == null)
    {
        FormThread = new Thread(FormStub);
        FormThread.SetApartmentState(ApartmentState.STA);
        FormThread.Start();
        mre.WaitOne();
    }
}

private void FormStub()
{
    Form = new ConnectorForm();
    mre.Set();
    Application.Run(Form);
}

Otros consejos

No realizar un spin-esperan en el hilo actual borrar toda la cuestión de la utilización de un hilo separado para Lanch la nueva forma? A menos que esté malentendido algo, lo que desea es crear la nueva forma sincrónica. (¿Hay alguna razón por la que tiene que residir en un STA diferente?)

Usted podría intentar lo siguiente, que utiliza una sola object / Monitor como el mecanismo de mensajes:

private void OpenForm()
{
    if (FormThread == null)
    {
        object obj = new object();
        lock (obj)
        {
            FormThread = new Thread(delegate () {
                lock (obj)
                {
                    Form = new ControllerForm();
                    Monitor.Pulse(obj);
                }
                Application.Run(Form);
            });
            FormThread.SetApartmentState(ApartmentState.STA);
            FormThread.Start();
            Monitor.Wait(obj);
        }
    }
}

El hilo original mantiene el bloqueo hasta que se llama Monitor.Wait; Esto permite que el segundo hilo (ya iniciado) para crear la forma, el pulso el hilo original de nuevo en la vida, y la liberación; el hilo original sale entonces sólo después existe Form.

tiendo a utilizar el AutoResetEvent para casos como éstos:

private AutoResetEvent _waitHandle = new AutoResetEvent(false);

private void OpenForm()
{
    Thread formThread = new Thread(FormStub);
    formThread.SetApartmentState(ApartmentState.STA);
    formThread.Start();
    _waitHandle.WaitOne();

    // when you come here FormStub has signaled                
}

private void FormStub()
{
    // do the work

    // signal that we are done
    _waitHandle.Set();
}

Otra manera de pasar un EventWaitHandle es pasarlo a FormStub como un parámetro (por lo que no recarga la modelo de objetos):

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    EventWaitHandle e = new EventWaitHandle(false, EventResetMode.ManualReset);
    Thread t = new Thread(FormStub);
    t.SetApartmentState(ApartmentState.STA);
    t.Start(e);
    e.WaitOne();
}

static void FormStub(object param)
{
    EventWaitHandle e = (EventWaitHandle) param;
    Form f = new Form1();

    e.Set();
    Application.Run(new Form1());
}

Utilice un static bool para marcar o no el formulario se ha cargado. Es atómica por lo que no necesitará de bloqueo.

En el código principal, justo hacer algo como

while(!formRun) { Thread.Sleep(100); }

La verdadera pregunta es ¿por qué haces esto? Por lo general, desea que el hilo principal para ejecutar cosas de interfaz gráfica de usuario, y las roscas secundarias para ejecutar código auxiliar. Si usted explica por qué lo necesita que tal vez podemos llegar a una mejor técnica.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top