Как вызвать метод формы из родительского потока?
От: mDmitriy Россия  
Дата: 17.09.14 10:33
Оценка:
Всем привет!

Есть примерно такой вот код (.NET 4.0):
public class ThreadUIClass1 : IDisposable
{
    private Thread _thread;
    private Form _myForm;

    public void RunThread()
    {
        _thread = new Thread(RunApp);
        _thread.Start();
    }

    private void RunApp(object obj)
    {
        _myForm = new Form();
        Application.Run(_myForm);
    }

    public void Dispose()
    {
        try
        {
            if (_myForm != null)
            {
                _myForm.Dispose();
            }
        }
        catch (Exception e)
        {
        }
    }
}

На методе Dispose, естественно, сваливается в Exception.
Как сделать правильно?
Re: Как вызвать метод формы из родительского потока?
От: Sharov Россия  
Дата: 17.09.14 11:00
Оценка: 2 (1) +1
Здравствуйте, mDmitriy, Вы писали:

D>Как сделать правильно?


тыц
Кодом людям нужно помогать!
Re: Как вызвать метод формы из родительского потока?
От: Danchik Украина  
Дата: 17.09.14 12:19
Оценка: 3 (1)
Здравствуйте, mDmitriy, Вы писали:

[Skip]

D>На методе Dispose, естественно, сваливается в Exception.

D>Как сделать правильно?

Вот так:

    public class ThreadUIClass1 : IDisposable
    {
        private Thread _thread;

        public void Dispose()
        {
            if (_thread != null)
            {
                _thread.Interrupt();
                _thread.Join(Int32.MaxValue);
                _thread = null;
            }
        }

        public void RunThread()
        {
            _thread = new Thread(RunApp);
            _thread.Start();
        }

        private void RunApp(object obj)
        {
            using (var myForm = new Form())
            {
                Application.Run(myForm);
            }
        }
    }
Re[2]: Как вызвать метод формы из родительского потока?
От: mDmitriy Россия  
Дата: 17.09.14 13:12
Оценка:
Здравствуйте, Sharov, Вы писали:
S> тыц
Спасибо, но это, кажется, несколько не то, что мне нужно...
Про BackgroundWorker, Invoke и BeginInvoke я в курсе, но они вызываются из формы, а мне надо доступ к форме снаружи.
Re[2]: Как вызвать метод формы из родительского потока?
От: mDmitriy Россия  
Дата: 17.09.14 13:21
Оценка:
Здравствуйте, Danchik, Вы писали:

D>Здравствуйте, mDmitriy, Вы писали:


D>[Skip]


D>>На методе Dispose, естественно, сваливается в Exception.

D>>Как сделать правильно?

D>Вот так:

using — это гениально, спасибо!
а в чем смысл этих строчек?
_thread.Interrupt();
_thread.Join(Int32.MaxValue);
Re[3]: Как вызвать метод формы из родительского потока?
От: Sharov Россия  
Дата: 17.09.14 13:29
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>а в чем смысл этих строчек?

D>
D>_thread.Interrupt();
D>_thread.Join(Int32.MaxValue); 
D>


Прерываем поток, и ждем пока он завершится.
Кодом людям нужно помогать!
Re[3]: Как вызвать метод формы из родительского потока?
От: Danchik Украина  
Дата: 17.09.14 13:40
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Здравствуйте, Danchik, Вы писали:


D>>Здравствуйте, mDmitriy, Вы писали:


D>>[Skip]


D>>>На методе Dispose, естественно, сваливается в Exception.

D>>>Как сделать правильно?

D>>Вот так:

D>using — это гениально, спасибо!
D>а в чем смысл этих строчек?
D>
D>_thread.Interrupt();
D>_thread.Join(Int32.MaxValue); 
D>


Interrupt тут мало поможет (недоглядел)
Идея в том чтобы мы таки дождались закрытия потока, а оно произойдет только когда вы закроете форму.
Так как я не знаю сценарий использования вашего Dispose, то предложил такой. Больше конкретики и можна подогнать под ваши потребности.
Re[4]: Как вызвать метод формы из родительского потока?
От: mDmitriy Россия  
Дата: 17.09.14 15:04
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, mDmitriy, Вы писали:


D>>а в чем смысл этих строчек?

D>>
D>>_thread.Interrupt();
D>>_thread.Join(Int32.MaxValue); 
D>>


S>Прерываем поток, и ждем пока он завершится.

Меня смутила эта строчка в MSDN про Interrupt : Прерывает работу потока, находящегося в состоянии WaitSleepJoin.
У меня-то поток с формой не блокирован, он активен.
Re[4]: Как вызвать метод формы из родительского потока?
От: mDmitriy Россия  
Дата: 17.09.14 15:12
Оценка:
Здравствуйте, Danchik, Вы писали:
D>>>Вот так:
D>>а в чем смысл этих строчек?
D>>
D>>_thread.Interrupt();
D>>_thread.Join(Int32.MaxValue); 
D>>

D>Interrupt тут мало поможет (недоглядел)
D>Идея в том чтобы мы таки дождались закрытия потока, а оно произойдет только когда вы закроете форму.
D>Так как я не знаю сценарий использования вашего Dispose, то предложил такой. Больше конкретики и можна подогнать под ваши потребности.
Поток с формой запускается при активизации COM+ объекта и отображает иконку в трее.
При удалении COM+ объекта надо бы форму высвободить — по идее, это должно привести к закрытию потока (ибо Application.Run).
С чем, собственно, и был связан мой первоначальный вопрос.
Re: Как вызвать метод формы из родительского потока?
От: Rinbe Россия  
Дата: 17.09.14 16:13
Оценка: 3 (1)
Здравствуйте, mDmitriy, Вы писали:

D>Всем привет!


D>Есть примерно такой вот код (.NET 4.0):

D>
D>public class ThreadUIClass1 : IDisposable
D>{
D>    private Thread _thread;
D>    private Form _myForm;

D>    public void RunThread()
D>    {
D>        _thread = new Thread(RunApp);
D>        _thread.Start();
D>    }

D>    private void RunApp(object obj)
D>    {
D>        _myForm = new Form();
D>        Application.Run(_myForm);
D>    }

D>    public void Dispose()
D>    {
D>        try
D>        {
D>            if (_myForm != null)
D>            {
D>                _myForm.Dispose();
D>            }
D>        }
D>        catch (Exception e)
D>        {
D>        }
D>    }
D>}
D>

D>На методе Dispose, естественно, сваливается в Exception.
D>Как сделать правильно?

Может вот так надо:

  public class ThreadUIClass1 : IDisposable
    {
        private Thread _thread;
        private Form _myForm;

        public void RunThread()
        {
            _thread = new Thread(RunApp);
            _thread.Start();
        }

        private void RunApp(object obj)
        {
            _myForm = new MainForm();
            Application.Run(_myForm);
        }

        public void Dispose()
        {
            Application.Exit();
        }
    }

Re[2]: Как вызвать метод формы из родительского потока?
От: mDmitriy Россия  
Дата: 18.09.14 04:22
Оценка:
Здравствуйте, Rinbe, Вы писали:

R>Может вот так надо:


R>
R>        public void Dispose()
R>        {
R>            Application.Exit();
R>        }
R>

R>
Идея хорошая!
А это не схлопнет родительский поток более раньше чем надо?
Re[3]: Как вызвать метод формы из родительского потока?
От: Fortnum  
Дата: 18.09.14 08:58
Оценка: 3 (1)
Здравствуйте, mDmitriy, Вы писали:

D>Здравствуйте, Sharov, Вы писали:

S>> тыц
D>Спасибо, но это, кажется, несколько не то, что мне нужно...
D>Про BackgroundWorker, Invoke и BeginInvoke я в курсе, но они вызываются из формы, а мне надо доступ к форме снаружи.

Абсолютно без разницы, откуда вызываются Invoke и BeginInvoke. В случае с формой, откуда бы эти методы ни были бы вызваны, всё что они сделают — поставят в очередь сообщений вызов заданного делегата. Разница лишь в том, что Invoke будет ждать, когда поставленная в очередь структура данных будет обработана — т.е. будет вызван делегат, и этот делегат вернет вызов обратно. А BeginInvoke не ждет — после постановки делегата в очередь немедленно возвращает вызов. Методы Invoke и BeginInvoke — это так называемые потоко-безопасные методы, т.е. их можно вызывать из неограниченного числа потоков одновременно — они на это и рассчитаны. Вот что по этому поводу написано в MSDN :

In addition to the InvokeRequired property, there are four methods on a control that are thread safe : Invoke, BeginInvoke, EndInvoke


Вообще, говорить, что метод вызывается "из формы" в данном случае некорректно. Вызов делает поток. Т.е. в чистом виде один и тот же код может параллельно исполняться неограниченным количеством потоков. Но код сам по себе — штука бесполезная. Код изменяет состояние, и вот тут нюанс. Если каждый из этих параллельных потоков работает со своим состоянием — проблем нет. Но если они в какой-то части работают над одним общим состоянием, для каждого отдельного потока могут возникнуть непредвиденные (недетерминированные) изменения этого общего состояния между последовательно исполняемыми им инструкциями, если этот отдельный поток не рассматривает это состояние как общее, либо не имеет возможности предотвратить такие изменения.

Состояние формы для многопоточной программы — это и есть то самое общее (разделяемое) состояние. Сделать разделяемое состояние детерминированным можно как минимум двумя путями:

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

Поэтому в ThreadUIClass1.Dispose надо всего лишь написать:

_myForm.Invoke((Action)_myForm.Dispose);


Но у тебя в коде еще и не такая явная Проблема №2. В ThreadUIClass1.Dispose у тебя идет проверка "if (_myForm != null)", но поле _myForm — это разделяемое состояние между потоком "new Thread(RunApp)" и потоком, который вызовет ThreadUIClass1.Dispose. И хотя, в спецификации C# в 5.5 Atomicity of variable references говорится:

Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types.


То есть запись и чтение поля _myForm — атомарные операции, и само содержимое этого поля будет всегда прочитано корректно, однако, в ~0.001% может получиться так, что создание формы задержится — можно представить себе это так:

private void RunApp(object obj)
{
    var myForm = new Form();

    Thread.Sleep(10000);

    _myForm = myForm;

    Application.Run(_myForm);
}


, а клиент во время этой задержки, до назначения значения полю _myForm успеет задиспоузить твой COM+ компонент, и теоретически может получиться ситуация, когда метод ThreadUIClass1.Dispose выполнится до назначения значения полю _myForm

Более того, даже если форма успеет создаться, и значение полю _myForm будет назначено, нюанс в том, что Application.Run запускает прокачку очереди сообщений, и если ты задиспоузишь форму после ее создания, но до вызова Application.Run... ну, попробуй сделать так и сам все увидишь :

private void RunApp(object obj)
{
    _myForm = new Form();

    _myForm.Dispose();

    Application.Run(_myForm);
}


Тут еще один нюанс, который состоит в том, что вызвать Invoke или BeginInvoke до создания хэндла окна Windows Forms не даст, а кинет исключение. Для создания хэндла окна надо, кстати, немного прокачать очередь сообщений. То есть исключение возникнет не в "new Thread" (не в RunApp), а в ThreadUIClass1.Dispose при вызове _myForm.Invoke. То есть компонент будет задиспоужен с исключением, а форма (если ты обработаешь это исключение) останется существовать. Попробуй еще так и увидишь:

private void RunApp(object obj)
{
    _myForm = new Form();

    _myForm.Invoke((Action)_myForm.Dispose);

    Application.Run(_myForm);
}


Короче, если интересно, можем обсудить, как правильно решать эту Проблему №2. К примеру, в COM специально для решения этой задачи существует single-threaded apartment — STA апартмент, который позволяет исполнять внешние вызовы последовательно. COM сам осуществляет синхронизацию внешних параллельных вызовов в последовательные. Конкретно эта модель апартмента аналогична окну, и даже представляет собой один поток и ту же самую очередь сообщений. И почему бы не использовать одну и ту же очередь сообщений, один и тот же рабочий поток для двух дел одновременно? Для обработки внешних вызовов, и для реакции на события окна? Этот апартмент специально для управления окнами и создавался. Минус в том, что ты потеряешь для своего компонента возможность вызова его методов в параллель, а обработка событий окна будет тормозить обработку внешних вызовов. Если у тебя стоит задача не формой управлять, а параллельно обслуживать большое число клиентов, а форма служит лишь для индикации работы, конечно решение с STA не подойдет, хотя...
Отредактировано 18.09.2014 9:32 fortnum . Предыдущая версия . Еще …
Отредактировано 18.09.2014 9:03 fortnum . Предыдущая версия .
Отредактировано 18.09.2014 9:00 fortnum . Предыдущая версия .
Отредактировано 18.09.2014 8:59 fortnum . Предыдущая версия .
Re[5]: Как вызвать метод формы из родительского потока?
От: Danchik Украина  
Дата: 18.09.14 09:47
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Здравствуйте, Danchik, Вы писали:

D>>>>Вот так:
D>>>а в чем смысл этих строчек?
D>>>
D>>>_thread.Interrupt();
D>>>_thread.Join(Int32.MaxValue); 
D>>>

D>>Interrupt тут мало поможет (недоглядел)
D>>Идея в том чтобы мы таки дождались закрытия потока, а оно произойдет только когда вы закроете форму.
D>>Так как я не знаю сценарий использования вашего Dispose, то предложил такой. Больше конкретики и можна подогнать под ваши потребности.
D>Поток с формой запускается при активизации COM+ объекта и отображает иконку в трее.
D>При удалении COM+ объекта надо бы форму высвободить — по идее, это должно привести к закрытию потока (ибо Application.Run).
D>С чем, собственно, и был связан мой первоначальный вопрос.

Тогда так:

    public class ThreadUIClass1 : IDisposable
    {
        private Form _form;
        private Thread _thread;

        public void Dispose()
        {
            TerminateThread();
        }

        private void TerminateThread()
        {
            if (_thread != null)
            {
                var form = _form;
                if (form != null && form.IsHandleCreated)
                    form.Invoke((MethodInvoker) (() => form.Close()));
                _thread.Join(Int32.MaxValue);
                _thread = null;
            }
        }

        public void RunThread()
        {
            _thread = new Thread(RunApp);
            _thread.Start();
        }

        private void RunApp(object obj)
        {
            _form = new Form();
            try
            {
                Application.Run(_form);
            }
            finally
            {
                _form.Dispose();
                _form = null;
            }
        }
    }
Отредактировано 18.09.2014 9:48 Danchik . Предыдущая версия .
Re[6]: Как вызвать метод формы из родительского потока?
От: Fortnum  
Дата: 18.09.14 10:02
Оценка: +1
Здравствуйте, Danchik, Вы писали:

D> var form = _form;

D> if (form != null && form.IsHandleCreated)
D> form.Invoke((MethodInvoker) (() => form.Close()));
D> _thread.Join(Int32.MaxValue);

Хм... а если _form != null, но form.IsHandleCreated еще не криэйтэд?
Re[6]: Как вызвать метод формы из родительского потока?
От: Fortnum  
Дата: 18.09.14 10:21
Оценка:
Здравствуйте, Danchik, Вы писали:

D>        private void TerminateThread()
D>        {
D>            ...
D>                if (form != null && form.IsHandleCreated)
D>                    form.Invoke((MethodInvoker) (() => form.Close()));
D>            ...
D>        }
...
D>        private void RunApp(object obj)
D>        {
D>            _form = new Form();
D>            try
D>            {
D>                Application.Run(_form);
D>            }
D>            finally
D>            {
D>                _form.Dispose();
D>                _form = null;
D>            }
D>        }
D>    }



Form.Close последним делом вызывает Form.Dispose, и отличается от Form.Dispose только логикой вопрошания разрешения закрытия (подразумевает возможность отказа), закрытие всяких связанных окон, и поэтому получается масло масляное
Re[5]: Как вызвать метод формы из родительского потока?
От: Fortnum  
Дата: 18.09.14 12:24
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Поток с формой запускается при активизации COM+ объекта и отображает иконку в трее.

D>При удалении COM+ объекта надо бы форму высвободить — по идее, это должно привести к закрытию потока (ибо Application.Run).
D>С чем, собственно, и был связан мой первоначальный вопрос.

А почему ты хочешь управлять формой из компонента? На мой взгляд — это не самое лучшее решение в плане архитектуры. Наверняка, ты еще из этой формы своим компонентом управляешь, или периодически мониторишь его состояние. Короче, у тебя, подозреваю, сейчас двунаправленная логическая связь "компонент"<->"форма". Гораздо лучше сделать так, чтобы либо компонент ничего не знал о форме, либо форма ничего не знала о компоненте. Т.к. в форме, не знающей ничего о компоненте, смысла нет, то получается, надо сделать так: "компонент"<-"форма". Т.е. компонент ничего не знает о форме, а форма знает о компоненте. Более того, форма для компонента, по сути, — тот же клиент. Поэтому я бы предложил решить вопрос ближе к такому варианту:

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

namespace WindowsFormsApplication1
{
    static class Program
    {
        static void Main()
        {
            Console.WriteLine("Клиент: нажми любую клавишу, чтобы подключиться к серверу."); Console.ReadKey(true);

            var threadUIClass1 = Task.Run(() => new ThreadUIClass1()).Result;

            Console.WriteLine("Клиент: нажми любую клавишу, чтобы отключиться от сервера."); Console.ReadKey(true);

            Task.Run(() => threadUIClass1.Dispose());

            Console.WriteLine("Press any key to exit.");
            Console.ReadKey(true);
        }

        public class ThreadUIClass1 : IDisposable
        {
            volatile int _data;

            volatile bool _disposed;

            public ThreadUIClass1()
            {
                new Thread(() =>
                {
                    while (!_disposed)
                    {
                        Task.Delay(100).Wait();

                        _data = new Random().Next(int.MinValue, int.MaxValue);
                    }

                    Console.WriteLine("Сервер: рабочий поток завершен."); 
                }).Start();

                Console.WriteLine("Сервер: рабочий поток запущен."); 

                new Thread(() => 
                {
                    var form = new Form();

                    form.Load += (o, e) =>
                    {
                        var timer = new System.Windows.Forms.Timer() { Interval = 5000 };

                        timer.Tick += (t, x) =>
                        {
                            form.Text = _data.ToString();

                            if (_disposed)
                            {
                                form.Dispose();
                            }
                        };

                        timer.Start();
                    };

                    Console.WriteLine("Сервер: локальный пользовательский интерфейс запущен."); 

                    Application.Run(form);

                    Console.WriteLine("Сервер: локальный пользовательский интерфейс остановлен."); 
                }).Start();
            }

            public void Dispose()
            {
                _disposed = true;

                Console.WriteLine("Сервер: удаленный клиент отсоединен."); 
            }
        }
    }
}
Отредактировано 18.09.2014 12:38 fortnum . Предыдущая версия . Еще …
Отредактировано 18.09.2014 12:26 fortnum . Предыдущая версия .
Re[6]: Как вызвать метод формы из родительского потока?
От: mDmitriy Россия  
Дата: 18.09.14 13:07
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>А почему ты хочешь управлять формой из компонента? На мой взгляд — это не самое лучшее решение в плане архитектуры. Наверняка, ты еще из этой формы своим компонентом управляешь, или периодически мониторишь его состояние. Короче, у тебя, подозреваю, сейчас двунаправленная логическая связь "компонент"<->"форма". Гораздо лучше сделать так, чтобы либо компонент ничего не знал о форме, либо форма ничего не знала о компоненте. Т.к. в форме, не знающей ничего о компоненте, смысла нет, то получается, надо сделать так: "компонент"<-"форма". Т.е. компонент ничего не знает о форме, а форма знает о компоненте. Более того, форма для компонента, по сути, — тот же клиент.

Не совсем так...
У меня все управление формой компонентом сводится к создать/удалить, т.е., компонент о форме не может не знать, т.к., управляет процессом он, а форма лишь отображает.
Для этого и был написан класс ThreadUIClass1.
В конструктор формы передается объект данных от компонента (в примере я этот момент "упростил") и дальше форма живет своей жизнью в своем потоке, опрашивая его по таймеру, если активна.
Весь вопрос — правильно ее отдиспозить, когда компонент завершает работу.
Re[7]: Как вызвать метод формы из родительского потока?
От: Fortnum  
Дата: 18.09.14 13:15
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>У меня все управление формой компонентом сводится к создать/удалить, т.е., компонент о форме не может не знать, т.к., управляет процессом он, а форма лишь отображает.

D>Для этого и был написан класс ThreadUIClass1.
D>В конструктор формы передается объект данных от компонента (в примере я этот момент "упростил") и дальше форма живет своей жизнью в своем потоке, опрашивая его по таймеру, если активна.
D>Весь вопрос — правильно ее отдиспозить, когда компонент завершает работу.

Так пусть компонент в этом объекте данных выставляет флаг, что всё, баста, данных больше не будет. А форма при очередном обновлении сама чпокнется. И управление формой компонентом сведется только к "создать".

И я не совсем понял, зачем был написан класс ThreadUIClass1 Есть компонент, форма, разделяемый компонентом и формой объект данных от компонента. А ThreadUIClass1 какую роль играет?
Re[8]: Как вызвать метод формы из родительского потока?
От: mDmitriy Россия  
Дата: 18.09.14 14:10
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Здравствуйте, mDmitriy, Вы писали:


D>>У меня все управление формой компонентом сводится к создать/удалить, т.е., компонент о форме не может не знать, т.к., управляет процессом он, а форма лишь отображает.

D>>Для этого и был написан класс ThreadUIClass1.
D>>В конструктор формы передается объект данных от компонента (в примере я этот момент "упростил") и дальше форма живет своей жизнью в своем потоке, опрашивая его по таймеру, если активна.
D>>Весь вопрос — правильно ее отдиспозить, когда компонент завершает работу.

F>Так пусть компонент в этом объекте данных выставляет флаг, что всё, баста, данных больше не будет. А форма при очередном обновлении сама чпокнется. И управление формой компонентом сведется только к "создать".


F>И я не совсем понял, зачем был написан класс ThreadUIClass1 Есть компонент, форма, разделяемый компонентом и формой объект данных от компонента. А ThreadUIClass1 какую роль играет?

ThreadUIClass1 — это оболочка над формой.
Чтобы не загромождать код самого компонента.
Re[9]: Как вызвать метод формы из родительского потока?
От: Fortnum  
Дата: 18.09.14 14:45
Оценка:
Здравствуйте, mDmitriy, Вы писали:

F>>И я не совсем понял, зачем был написан класс ThreadUIClass1 Есть компонент, форма, разделяемый компонентом и формой объект данных от компонента. А ThreadUIClass1 какую роль играет?

D>ThreadUIClass1 — это оболочка над формой.
D>Чтобы не загромождать код самого компонента.

То есть компонент создает экземпляр ThreadUIClass1, экземпляр ThreadUIClass1 создаёт форму? Если класс существует только и исключительно "для не загромождения кода", лучше использовать partial
Re[10]: Как вызвать метод формы из родительского потока?
От: mDmitriy Россия  
Дата: 19.09.14 04:20
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Здравствуйте, mDmitriy, Вы писали:


F>>>И я не совсем понял, зачем был написан класс ThreadUIClass1 Есть компонент, форма, разделяемый компонентом и формой объект данных от компонента. А ThreadUIClass1 какую роль играет?

D>>ThreadUIClass1 — это оболочка над формой.
D>>Чтобы не загромождать код самого компонента.

F>То есть компонент создает экземпляр ThreadUIClass1, экземпляр ThreadUIClass1 создаёт форму? Если класс существует только и исключительно "для не загромождения кода", лучше использовать partial

Ну, код еще в процессе создания и будет еще рефакторится не раз.
Сейчас ThreadUIClass1 лежит вообще в отдельной dll вместе с формой.
Re: Как вызвать метод формы из родительского потока?
От: Clerk  
Дата: 19.09.14 07:07
Оценка:
Здравствуйте, mDmitriy, Вы писали:

  public class FormManager : IDisposable
  {
    Form    _form;
    Thread  _thread;

    public FormManager()
    {
      _form = new Form();
      _thread = new Thread(
        () => 
        {
          using (_form)
          {
            _form.ShowDialog();
          }
        });

      _thread.Start();
    }

    bool _disposed = false;

    public void Close()
    {
      if (!_disposed)
      {
        _disposed = true;

        // be sure that handle is there
        while (!_form.IsHandleCreated)
          Thread.Sleep(10);

        _form.Invoke((Action)(() => _form.Close()));

        while (_thread.ThreadState != ThreadState.Stopped)
          Thread.Sleep(1);
      }
    }

    public void Dispose()
    {
      Close();
    }
  }
Re[6]: Как вызвать метод формы из родительского потока?
От: Danchik Украина  
Дата: 19.09.14 12:24
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Здравствуйте, mDmitriy, Вы писали:


D>>Поток с формой запускается при активизации COM+ объекта и отображает иконку в трее.

D>>При удалении COM+ объекта надо бы форму высвободить — по идее, это должно привести к закрытию потока (ибо Application.Run).
D>>С чем, собственно, и был связан мой первоначальный вопрос.

F>А почему ты хочешь управлять формой из компонента? На мой взгляд — это не самое лучшее решение в плане архитектуры. Наверняка, ты еще из этой формы своим компонентом управляешь, или периодически мониторишь его состояние. Короче, у тебя, подозреваю, сейчас двунаправленная логическая связь "компонент"<->"форма". Гораздо лучше сделать так, чтобы либо компонент ничего не знал о форме, либо форма ничего не знала о компоненте. Т.к. в форме, не знающей ничего о компоненте, смысла нет, то получается, надо сделать так: "компонент"<-"форма". Т.е. компонент ничего не знает о форме, а форма знает о компоненте. Более того, форма для компонента, по сути, — тот же клиент. Поэтому я бы предложил решить вопрос ближе к такому варианту:


[Skip]
Зачем так наворачивать... Два потока, да и слипы ганять.
Я с барьерами памяти, не игрался, вот попроще, на скорую руку

public class ThreadUIClass1 : IDisposable
    {
        private volatile Form _form;
        private Thread _thread;
        private object _syncObj = new object();

        public void Dispose()
        {
            TerminateThread();
        }

        private void TerminateThread()
        {
            if (_thread != null)
            {
                Thread.Sleep(1000);

                Form form;
                lock (_syncObj)
                {
                    form = _form;
                }
                if (form != null && form.IsHandleCreated)
                    form.Invoke((MethodInvoker) (() => form.Close()));

                _thread.Join(Int32.MaxValue);
                _thread = null;
            }
        }

        public void RunThread()
        {
            _thread = new Thread(RunApp);
            _thread.Start();
        }

        private void RunApp(object obj)
        {
            var form = new Form();
            try
            {
                form.Show();
                lock (_syncObj)
                {
                    _form = form;
                }
                Application.Run(form);
            }
            finally
            {
                lock (_syncObj)
                {
                    using (_form)
                        _form = null;
                }
            }
        }
    }
Re[7]: Как вызвать метод формы из родительского потока?
От: drol  
Дата: 19.09.14 12:34
Оценка:
Здравствуйте, Danchik, Вы писали:

D> private volatile Form _form;


В такой реализации — на lock'ах — volatile не нужен.
Re[7]: Как вызвать метод формы из родительского потока?
От: mDmitriy Россия  
Дата: 19.09.14 12:42
Оценка: -1
Здравствуйте, Danchik, Вы писали:

D>[Skip]

D>Зачем так наворачивать... Два потока, да и слипы ганять.
D>Я с барьерами памяти, не игрался, вот попроще, на скорую руку
lock мне таки не нравится — это же не интенсивное многопоточное взаимодействие.
Re[8]: Как вызвать метод формы из родительского потока?
От: Danchik Украина  
Дата: 19.09.14 12:49
Оценка: +1
Здравствуйте, mDmitriy, Вы писали:

D>Здравствуйте, Danchik, Вы писали:


D>>[Skip]

D>>Зачем так наворачивать... Два потока, да и слипы ганять.
D>>Я с барьерами памяти, не игрался, вот попроще, на скорую руку
D>lock мне таки не нравится — это же не интенсивное многопоточное взаимодействие.

Тоесть крутить циклы со слипами это гут, а ДВА раза вызвать лок, это плохо? Где логика?
Re[8]: Как вызвать метод формы из родительского потока?
От: drol  
Дата: 19.09.14 12:59
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>lock мне таки не нравится — это же не интенсивное многопоточное взаимодействие.


Наоборот. Как раз потому что в задаче разовая операция именно lock для неё само то.
Re[7]: Как вызвать метод формы из родительского потока?
От: Fortnum  
Дата: 19.09.14 16:12
Оценка:
Здравствуйте, Danchik, Вы писали:

D>Зачем так наворачивать... Два потока, да и слипы ганять.


Там помимо Main() три потока Моделирование рабочего потока, который _data обновляет. Других "слипов" в коде нигде нет — где ты их увидел, интересно . Еще UI-поток формы, и поток клиента COM-компонента (Task.Run в Main, через ThreadPool то бишь).

D>Я с барьерами памяти, не игрался, вот попроще, на скорую руку


Есть несколько на скорую руку вопросов по твоему коду. Вот некоторые из них.

Первый Вариант с двумя и более клиентскими потоками одного COM-компонента (одного экземпляра ThreadUIClass1) ты не рассматривал или просто не доделал? Я просто смотрю, как минимум _thread не защищена от этого.

Второй Зачем вообще диспоузить форму после выхода из Application.Run, если ей на тот момент уже был вызван Close, что подразумевает Dispose?

D>            var form = new Form();
D>            try
D>            {
D>                lock (_syncObj)
D>                {
D>                    _form = form;
D>                }
D>                Application.Run(form);
D>            }
D>            finally
D>            {
D>                lock (_syncObj)
D>                {
D>                    using (_form)
D>                        _form = null;
D>                }
D>            }


Третий Возможна такая ситуация, что вызов клиенту из RunThread уже вернется, и он тут же вызовет ThreadUIClass1.Dispose. Если это произойдет до присвоения значения _form = form, а произойти это может, т.к. Form.Show подразумевает вызов Form.Load, куда обычно навешивается много чего, тогда вызов TerminateThread проследует напрямую к _thread.Join(Int32.MaxValue), и форма останется висеть навечно.

Четвертый Зачем аргумент Int32.MaxValue в _thread.Join? Ты на самом деле надеешься, что кто-то или что-то будет ждать 24.855 суток?

Пятый Какую задачу выполняет вот этот Sleep?

D>        private void TerminateThread()
D>        {
D>            if (_thread != null)
D>            {
D>                Thread.Sleep(1000);


Есть еще вопросы, но они зависят от твоих ответов, так что пока всё
Re[9]: Как вызвать метод формы из родительского потока?
От: Fortnum  
Дата: 19.09.14 16:16
Оценка:
Здравствуйте, Danchik, Вы писали:

D>>>[Skip]

D>>>Зачем так наворачивать... Два потока, да и слипы ганять.
D>>>Я с барьерами памяти, не игрался, вот попроще, на скорую руку
D>>lock мне таки не нравится — это же не интенсивное многопоточное взаимодействие.
D>Тоесть крутить циклы со слипами это гут, а ДВА раза вызвать лок, это плохо? Где логика?

Во-во, я тебя и спрашиваю здесь
Автор: Fortnum
Дата: 19.09.14
, где ты там "слипы"-то увидал?
Re[9]: Как вызвать метод формы из родительского потока?
От: Fortnum  
Дата: 19.09.14 16:27
Оценка:
Здравствуйте, Danchik, Вы писали:

D>>>[Skip]

D>>>Зачем так наворачивать... Два потока, да и слипы ганять.
D>>>Я с барьерами памяти, не игрался, вот попроще, на скорую руку
D>>lock мне таки не нравится — это же не интенсивное многопоточное взаимодействие.
D>Тоесть крутить циклы со слипами это гут, а ДВА раза вызвать лок, это плохо? Где логика?

Логику, кстати, я пояснял: превратить двунаправленную логическую связь "компонент"<->"форма" в однонаправленную "компонент"<-"форма". Иначе, по сути, ты (а) запихиваешь контроллер формы в другой поток, (б) жестко увязываешь какой-то AppServer-компонент с UI — и то и другое неправильно. Если это не AppServer-компонент, а компонент, специально предназначенный для управления этой формы, тогда вообще не надо никаких потоков — для этого существует STA. На всякий случай и тут еще раз напишу — "слипов" нету.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.