É possível “desmembrar” vários threads da GUI?(Não interrompendo o sistema em Application.Run)
Pergunta
Meu gol
Eu gostaria de ter um thread de processamento principal (não GUI) e ser capaz de desmembrar GUIs em seus próprios threads de segundo plano, conforme necessário, e fazer com que meu thread principal não GUI continue funcionando.Dito de outra forma, quero que meu thread principal não GUI seja o proprietário do thread GUI e não vice-versa.Não tenho certeza se isso é possível com o Windows Forms (?)
Fundo
Eu tenho um sistema baseado em componentes no qual um controlador carrega dinamicamente assemblies e instancia e executa classes implementando um comum IComponent
interface com um único método DoStuff()
.
Quais componentes carregados são configurados por meio de um arquivo de configuração xml e pela adição de novos assemblies contendo diferentes implementações de IComponent
.Os componentes fornecem funções utilitárias para o aplicativo principal.Enquanto o programa principal está fazendo isso, por ex.controlando uma usina nuclear, os componentes podem estar executando tarefas utilitárias (em seus próprios threads), por exemplo.limpando o banco de dados, enviando e-mails, imprimindo piadas engraçadas na impressora, o que quer que seja.O que eu gostaria é que um desses componentes fosse capaz de exibir uma GUI, por exemplo.com informações de status para o referido componente de envio de e-mail.
A vida útil do sistema completo é assim
- O aplicativo é iniciado.
- Verifique o arquivo de configuração para carregar os componentes.Carregue-os.
- Para cada componente, execute
DoStuff()
para inicializá-lo e fazê-lo viver sua própria vida em seus próprios threads. - Continue a fazer o trabalho principal do aplicativo, para sempre.
Ainda não consegui executar com êxito o ponto 3 se o componente iniciar uma GUI em DoStuff()
.Ele simplesmente para até que a GUI seja fechada.E só depois que a GUI for fechada o programa avança para o ponto 4.
Seria ótimo se esses componentes pudessem iniciar suas próprias GUIs do Windows Forms.
Problema
Quando um componente tenta iniciar uma GUI em DoStuff()
(a linha exata do código é quando o componente é executado Application.Run(theForm)
), o componente e, portanto, nosso sistema "trava" no Application.Run()
linha até que a GUI seja fechada.Bem, a GUI recém-iniciada funciona bem, como esperado.
Exemplo de componentes.Um não tem nada a ver com GUI, enquanto o segundo abre janelas fofas com coelhinhos rosa fofos.
public class MyComponent1: IComponent
{
public string DoStuff(...) { // write something to the database }
}
public class MyComponent2: IComponent
{
public void DoStuff()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form());
// I want the thread to immediately return after the GUI
// is fired up, so that my main thread can continue to work.
}
}
Eu tentei isso sem sorte.Mesmo quando tento iniciar a GUI em seu próprio thread, a execução é interrompida até que a GUI seja fechada.
public void DoStuff()
{
new Thread(ThreadedInitialize).Start()
}
private void ThreadedInitialize()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form());
}
É possível desmembrar uma GUI e retornar depois Application.Run()
?
Solução
Aplicativo.Executar O método exibe um (ou mais) formulários e inicia o loop de mensagem padrão que é executado até que todos os formulários sejam fechados.Você não pode forçar o retorno desse método, exceto fechando todos os seus formulários ou forçando o encerramento do aplicativo.
Você pode, no entanto, passar um ApplicationContext (instad de um novo Form()) para o método Application.Run e ApplicationContext pode ser usado para iniciar vários formulários ao mesmo tempo.Sua inscrição só terminará quando todas elas forem encerradas.Veja aqui: http://msdn.microsoft.com/en-us/library/system.windows.forms.application.run.aspx
Além disso, quaisquer formulários que você mostrar de forma não modal continuarão a ser executados junto com o formulário principal, o que permitirá que você tenha mais de uma janela que não bloqueie umas às outras.Eu acredito que isso é realmente o que você está tentando realizar.
Outras dicas
Tenho certeza de que isso é possível se você tentar com força suficiente, mas sugiro que não é uma boa ideia.
As 'Windows' (que você vê na tela) estão altamente acopladas aos processos.Ou seja, espera-se que cada processo que exibe qualquer GUI tenha um Message Loop, que processa todas as mensagens envolvidas na criação e gerenciamento de janelas (coisas como 'cliquei no botão', 'fechei o aplicativo', 'redesenhei a tela ' e assim por diante.
Por causa disso, presume-se mais ou menos que, se você tiver algum loop de mensagem, ele deverá estar disponível durante a vida útil do seu processo.Por exemplo, o Windows pode enviar uma mensagem de 'sair' e você precisa ter um loop de mensagens disponível para lidar com isso, mesmo que não haja nada na tela.
Sua melhor aposta é fazer assim:
Faça uma forma falsa que nunca é mostrada, qual é o seu aplicativo 'principal' Start -up Call Application.Run e passe nessa forma falsa.Faça seu trabalho em outro thread e dispare eventos no thread principal quando precisar fazer coisas do Gui.
Não tenho certeza se isso está certo, no entanto, lembro-me de executar formulários de janela a partir de um aplicativo de console apenas atualizando o formulário e chamando newForm.Show() nele, se seus componentes usarem isso em vez de Application.Run() então o novo formulário não deve bloquear.
É claro que o componente será responsável por manter uma referência aos formulários que ele cria