Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > .NET для новичков > Форма в отдельном потоке


Автор: Enteropoly 2.8.2009, 23:06
Привет!

Вопрос достаточно простой, задаю по причине того, что сегодня необычайно лень думать =)) 

Надобно мне, чтобы форма работала целиком в отдельном потоке. Есть, к примеру, GUI Thread, в котором месится главная форма, вот мне хочется запустить другую форму в другом потоке =)

Делаю так:
Код

private Layer3 _layer3 = null;
private Thread _thread3 = null;

//в конструкторе
this._thread3 = new Thread(new ThreadStart(this.StartLayer3));
this._thread3.Start();

private void StartLayer3()
        {
            this._layer3 = new Layer3(this);
            this._layer3.Show();
        }


Не работает =) Подозреваю, что форма создаётся, но поток тут же завершается. Есть мнение, что нужно применить что-то типа while (true), чтобы поток не завершался, но как это сделать в случае с формой? =)

Заранее благодарю за внимание  smile 

ЗЫ. Есть одно условие. С главной формы обязательно нужно иметь возможность обращаться к этой форме в другом потоке.

Автор: Экскалупатор 2.8.2009, 23:33
Enteropoly, если не секрет, то для чего такие сложности?

Автор: Enteropoly 2.8.2009, 23:56
Конечно не секрет, всё давно уже описано =)

http://forum.vingrad.ru/forum/topic-266901.html

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

ЗЫ. А в целом, для производительности. Рисовать на двух формах в двух потоках, думается мне, быстрее, чем на двух формах в одном потоке =)

Автор: Экскалупатор 3.8.2009, 00:34
Enteropoly, далеко не всегда, если это будет работать в системе где будет не один процессор, то да, выигрыш будет, но если проц один, то и обрабатывать он будет все потоку по очереди, а не одновременно, плюс затраты на переключение между потоками, все это требует доп ресурсов и как результат будет работать медленнее чем в одном потоке.

Автор: Enteropoly 3.8.2009, 00:44
Ну, в принципе, планируется сделать поддержку и одно-, и многоядерных процессоров =)

Итак, что тут я подумал-то... Есть ведь такая штука, которая называется Pipelining (кто знаком с процессорной архитектурой RISC, поймёт сразу). Заключается она в том, что:
- есть поток А и поток Б
- поток А служит приёмником информации и инициатором действий
- поток Б отвечает за исполнение этих самых действий.

Проще говоря, есть форма А в потоке А. Клик на форму А (приём информации) инициирует некое действие (например, перерисовать форму). У формы Б в потоке Б есть буфер, куда помещаются команды от формы А и постоянно выполняющийся метод, который и служит для того, чтобы исполнять команды.

Ещё проще говоря, поток А помещает в буфер потока Б какую-нибудь команду. Поток Б постоянно смотрит в свой буфер и если видит, что надо что-то делать, делает это в своём потоке =) Потокобезопасность на высоте, все довольны и счастливы  smile 

Теоретически (!) должно сработать =) Но проверю попозже. Такие штуки без зелья не делаются =))  smile 

ЗЫ. Само собой, количество обрабатывающий потоков может быть не ограничено. Что как бы добавляет серьёзную ложку мёда в такую немаловажную вещь, как расширяемость.

Автор: Enteropoly 3.8.2009, 01:28
Переписал пример, может кому-нибудь потом пригодится =)  smile 

Пайплайн
Код

    //команда конвейера
    //
    public struct PipelineCommand
    {
        public string Name;
        public object[] Params;
    }

    //конвейер обработки информации
    //
    public class Pipeline
    {
        private Queue<PipelineCommand> _pcQueue;
        private PipelineCommand _pcCurrent;
        private bool _isAlive = true;
        private bool _isPaused = false;

        public Pipeline()
        {
            this._pcQueue = new Queue<PipelineCommand>() ;
        }
        public Pipeline(int BufferSize)
        {
            this._pcQueue = new Queue<PipelineCommand>(BufferSize);
        }

        public void Start(int Performance)
        {
            while (this._isAlive)
            {
                Thread.Sleep(Performance);

                if (!this._isPaused)
                {
                    if (this._pcQueue.Count != 0)
                    {
                        this._pcCurrent = this._pcQueue.Dequeue();

                        switch (this._pcCurrent.Name)
                        {
                            case "ShowMSGBox":
                                MessageBox.Show((string)this._pcCurrent.Params[0]);
                                break;
                        }
                    }
                }
            }
        }

        public void Enqueue(PipelineCommand PC)
        {
            this._pcQueue.Enqueue(PC);
        }

        public void Pause(bool Set)
        {
            this._isPaused = Set;
        }

        public void Stop()
        {
            this._isAlive = false;
        }

        public PipelineCommand ShowNext()
        {
            if (this._pcQueue.Count != 0)
            {
                return this._pcQueue.Peek();
            }
            else
            {
                return new PipelineCommand { Name = "", Params = null };
            }
        }
    }


Этот код мы помещаем в форму, которая будет работать в потоке Б (исполнять команды)
Код

private Pipeline _pipe = new Pipeline();

        public Form(out Pipeline Pipe)
        {            
            Pipe = this._pipe;

            //...

            this._pipe.Start(100);
        }

 ВНИМАНИЕ!  Из-за того, что конвейер запускается прямо из конструктора, экземпляр формы НЕ СОЗДАСТСЯ и вы не сможете к нему обращаться! Но это и не нужно, поскольку вы делаете финт ушами, предоставляя управляющему классу сам конвейер, а с помощью конвейерных команд можно управлять этой же самой формой как угодно.

А так мы создаём форму Б из формы А (раздающей команды)
Код

private Pipeline _pipe = null;
private Thread _thread = null;

public Form()
        {
            Thread.CurrentThread.Name = "MainThread";

            this._thread = new Thread(new ThreadStart(this.StartAnotherForm));            
            this._thread.Start();

            //...
         }

private void StartAnotherForm()
        {            
           Form _f = new Form(out this._pipe);            
        }


Послать команду из формы А в форму Б очень просто
Код

this._pipe.Enqueue(new PipelineCommand
            {
                Name = "ShowMSGBox",
                Params = new object[] { Thread.CurrentThread.Name }
            });


Всё работает как часы =) Вспомогательные формы можно ставить на паузу (Pipeline.Pause(true)), а отключать, вызвав метод Pipeline.Stop()

Это не отражено в примере, но, как можно было бы догадаться, в конструктор Pipeline нужно так же передавать либо Control, либо, интерфейс, чтобы можно было к чему-либо обращаться.

Автор: Enteropoly 3.8.2009, 01:56
Да, кому интересно, эту идею (не реализацию) я нашёл в бесплатном документике Andrew D. Birrell, An Introduction to Programming with C# Threads, который можно скачать http://www.programminglearn.com/234/an-introduction-to-programming-with-c-threads. Много интересного про потоки (но нужно знать английский).

Апдейт. Поменял примерчик в посте выше. Теперь тему можно благополучно забыть и оставить для потомков  smile 

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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)