Как открыть форму в ветке и заставить ее оставаться открытой - PullRequest
8 голосов
/ 03 октября 2008

У меня все еще есть проблемы с выяснением того, как создавать winforms в отдельном потоке пользовательского интерфейса, который я обсуждал здесь .

Чтобы понять это, я написал следующую простую тестовую программу. Я просто хочу, чтобы он открыл форму в отдельном потоке с именем «Поток пользовательского интерфейса» и продолжал работу потока, пока форма открыта, в то же время позволяя пользователю взаимодействовать с формой (вращение обманывает). Я понимаю, почему не удается выполнить приведенное ниже действие, и поток немедленно закрывается, но я не уверен, что мне следует сделать, чтобы это исправить.

using System;
using System.Windows.Forms;
using System.Threading;

namespace UIThreadMarshalling {
    static class Program {
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var tt = new ThreadTest();
            ThreadStart ts = new ThreadStart(tt.StartUiThread);
            Thread t = new Thread(ts);
            t.Name = "UI Thread";
            t.Start();
            Thread.Sleep(new TimeSpan(0, 0, 10));
        }

    }

    public class ThreadTest {
        Form _form;
        public ThreadTest() {
        }

        public void StartUiThread() {
            _form = new Form1();
            _form.Show();
        }
    }
}

Ответы [ 6 ]

13 голосов
/ 03 октября 2008

В новом потоке вызовите Application.Run, передав объект формы, это заставит поток запустить свой собственный цикл обработки сообщений, пока окно открыто.

Затем вы можете вызвать .Join в этом потоке, чтобы заставить ваш основной поток подождать, пока поток пользовательского интерфейса не завершит свою работу, или использовать аналогичный прием для ожидания завершения этого потока.

Пример:

public void StartUiThread()
{
    using (Form1 _form = new Form1())
    {
        Application.Run(_form);
    }
}
3 голосов
/ 03 октября 2008

Я думаю, что ваша проблема с этой мыслью: «открыть форму в отдельном потоке с именем« поток пользовательского интерфейса »»

То, как работает Windows, выглядит следующим образом (обратите внимание, Vista может изменить некоторые из этих реалий):

Существует один важный поток, который называется " Main Thread " или " UI Thread ". Эта нить обрабатывает оконных сообщений , например, "эй, щелкнула мышкой по этому пикселю."

Эти сообщения попадают в очередь, и главный поток обрабатывает их , когда он не занят чем-то другим .

Таким образом, если вы выполняете вызов функции foo () в главном потоке, если это занимает много времени, сообщения Windows не обрабатываются в течение этого времени, и поэтому взаимодействие с пользователем не может происходить.

Основной поток также рисует пользовательский интерфейс на экране, поэтому длительное выполнение функции foo () также остановит рисование вашего приложения.

Все остальные потоки, кроме этого священного и специального основного потока, являются рабочими потоками. Эти рабочие потоки могут что-то делать, но они никогда не могут напрямую взаимодействовать с пользовательским интерфейсом.

Эта реальность вызывает две проблемы:

  1. ПОЛУЧЕНИЕ ОСНОВНОЙ РЕЗЬБЫ: Поскольку вы не хотите, чтобы долго выполняющаяся функция foo () прекращала все взаимодействие с пользователем, вам необходимо отправить эту работу в рабочий поток.

  2. ПОЛУЧЕНИЕ ВЕРНУТЬСЯ В ГЛАВНУЮ РЕЗЬБУ: Когда завершается долго выполняющаяся функция foo (), вы, вероятно, захотите уведомить пользователя, сделав что-то в пользовательском интерфейсе, но вы не можете сделать это в рабочем потоке, поэтому вам нужно «вернуться» к основному потоку.

Так что я считаю, что ваша проблема в вышеприведенной программе очень общая: сама ваша цель неверна, потому что не предполагается, что можно вызвать _form.Show () в каком-либо потоке, кроме священного основного потока.

3 голосов
/ 03 октября 2008
private void button1_Click(object sender, EventArgs e)
{
    var t = new Thread(RunNewForm);
    t.Start();
}
public static void RunNewForm()
{
     Application.Run(new Form2());
}
2 голосов
/ 03 октября 2008

Вы не можете открыть форму графического интерфейса пользователя ни в каком потоке, потому что в ней будет отсутствовать насос сообщений. Вы должны явно запустить насос сообщений в этом потоке, вызвав Application.Run () в методе потока. Другой вариант - вызвать DoEvents () в цикле, если вам нужно сделать что-то еще, потому что после Application.Run () этот поток будет ожидать, когда пользователь закроет форму в этой точке выполнения.

1 голос
/ 03 октября 2008

Я думаю, что просто вызов ShowDialog вместо Show поможет. Кажется, проблема в том, что поток завершает работу сразу после вызова Show, после чего сборщик мусора Form get. ShowDialog остановит поток, но все равно выполнит в нем события формы, поэтому поток продолжит работать, пока форма не будет закрыта.

Обычно я бы сделал это наоборот. Запустите форму в начальном потоке и запустите фоновые потоки, если вы хотите запустить длительные фоновые задачи.

Я также прочитал ваш второй вопрос, но не смог понять, что вы пытаетесь сделать. MVP-архитектура не требует от вас запуска бизнес-логики в разных потоках. Многопоточность сложно сделать правильно, поэтому я бы использовал несколько потоков только в том случае, если они мне действительно нужны.

0 голосов
/ 01 декабря 2015

Вместо вызова show () в форме, которая будет выполняться в форме, а затем просто закрываться в конце выполнения потока в функции StartUiThread (), вы можете заблокировать поток, пока форма не будет остановлена ​​в методе, так как просто блокируют другой поток. Пример:

public void StartUiThread() {
        _form = new Form1();
        _form.ShowDialog(); //Change Show() to ShowDialog() to wait in thread
    }

Это заставит новый поток ждать, пока диалог не закроется. Я не знаю, решит ли это ваши проблемы, но это решило мои.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...