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


Автор: bobadd 14.11.2007, 18:57
Проблема следующая. Делаю достаточно большую прогулину, необходимо чтобы на заднем плане в одной из закладок программа считывала данные с COM-порта.
Создал объект         
static BackgroundWorker bw = new BackgroundWorker();

Пока конечно не стал делать считывалку с ком-порта. Пока я убедился в том, что действительно работает на заднем плане второй поток. Но наткнулся на проблему, не могу вернуть данные назад в мою форму.
Т.е. считать то я считаю с com-порта, а чтобы вывести данные, например, в RichTextBox в главной форме... вообщем тут возникла проблема.

Вот примерный код
Код

   public partial class Form1 : Form
    {
        static BackgroundWorker bw = new BackgroundWorker();
...

        private void checkBox2_CheckedChanged(object sender, EventArgs e)
        {

            //Посылаем объект в другой поток
             bw.RunWorkerCompleted += bw_RunWorkerCompleted;
            bw.DoWork += bw_DoWork;
            bw.RunWorkerAsync(Terminal);   
            //
        }
...

        static void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            // Это будет вызвано рабочим потоком
            while (true) // Данный цикл необходим для постоянной прослушки Com-порта
            {
                System.Threading.Thread.Sleep(2000);
                MessageBox.Show("!!!!!!!!!!!", "Ошибка",   MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }



Вообщем суть проблемы в том, что мне из bw_DoWork нужно как-то добраться до Form1. Собственно говоря, мне нужно добраться до sender'a, насколько я понимаю. Только вот как?
Подскажите пожалуйста

Автор: tol05 14.11.2007, 19:37
Не очень понятно что значит "добраться". Если нужен доступ к WinForms контролу (или форме, не важно), то нужно использовать механизм Invoke. Это уже неоднократно обсуждалось... Нужно поиском по сайту пользоваться просто.

для примера, допустим что нужно из bw что-то записывать в 
Цитата(bobadd @  14.11.2007,  17:57 Найти цитируемый пост)
RichTextBox в главной форме

Код

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        static BackgroundWorker bw = new BackgroundWorker();

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            bw.RunWorkerCompleted += bw_RunWorkerCompleted;
            bw.DoWork += bw_DoWork;
            bw.RunWorkerAsync();
        }

        private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bw.Dispose();
        }

        private delegate void RichTextBoxUpdater(string message);


        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            while(true)
            {
                System.Threading.Thread.Sleep(2000);
                richTextBox1.Invoke(new RichTextBoxUpdater(richTextBox1.AppendText), new object[]{"I'm live!\r\n"});
            }
        }
    }


Автор: ivashkanet 14.11.2007, 20:04
tol05, нее, BW настолько грамотный контрол (он действительно контрол, а не просто класс), что никакие инвоки ему не нужны.

У него есть еще несколько событий (кроме DoWork), вот одно из них и позволяет передавать данные из потока "наружу".

Вот http://forum.vingrad.ru/index.php?show_type=forum&showtopic=124690&kw=progressbar&hl=progress пример кода работы с Воркером (где-то на форуме была ссылка на неплохую RSDN-овскую статью про него).

Добавлено через 1 минуту и 47 секунд
Нашел: http://www.rsdn.ru/article/dotnet/WinForms20.xml

Автор: tol05 14.11.2007, 20:08
ну не знаю... я вообще-то принципиальный противник воркера, уже заявлял об этом.. smile
Но вот я убрал Invoke и вот что получил
Код

System.InvalidOperationException was unhandled by user code
  Message="Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on."
  Source="System.Windows.Forms"
  StackTrace:
       at System.Windows.Forms.Control.get_Handle()
       at System.Windows.Forms.TextBoxBase.GetSelectionStartAndLength(Int32& start, Int32& length)
       at System.Windows.Forms.TextBoxBase.AppendText(String text)
       at WindowsApplication4.Form1.bw_DoWork(Object sender, DoWorkEventArgs e) in E:\TempFolder\WindowsApplication2\WindowsApplication4\Form1.cs:line 40
       at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
       at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)

код
Код

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            while(true)
            {
                System.Threading.Thread.Sleep(2000);
                richTextBox1.AppendText("I'm live!\r\n");
                //richTextBox1.Invoke(new RichTextBoxUpdater(richTextBox1.AppendText), new object[] { "I'm live!\r\n" });
            }
        }


Автор: 1stain 14.11.2007, 20:54
tol05, потому что DoWork() работает таки в другом потоке, а зато если BW кинет мессадж, а форма его перехватит - то все будет Ок.
Попробуй так:
Код

        public Form1()
        {
            InitializeComponent();

            countBackgroundWorker.RunWorkerAsync(10);
            this.countBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(countBackgroundWorker_ProgressChanged);
        }

        private void countBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int countTo = (int)e.Argument;
            for (int i = 1; i <= countTo; ++i)
            {
                countBackgroundWorker.ReportProgress( i*100/countTo, String.Format("i'm {0} miliseconds ago{1}", i*100, Environment.NewLine));
                System.Threading.Thread.Sleep(100);
            }
        }
        private void countBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            richTextBox1.AppendText((string)e.UserState);
        }


то есть BW через метод ReportProgress может кинуть форме сообщение, в аргумент которого засунет что захочет, а форма этот мессадж перехватит и обработает...

2ТС: сделай так, чтобы BW читал данные из com, если надо - обрабатывал и через посылал их во 2-м аргументе метода ReportProgress
форма подписывается на событие BW ProgressChanged, приводит ProgressChangedEventArgs.UserState к нужному типу и дальше делай с этими данными на форме что хочешь...

Автор: bobadd 14.11.2007, 22:44
Спасибо всем огромное, завтра приду на работу обязательно попробую!

Автор: tol05 14.11.2007, 23:44
1stain, да я как бы знал, что в другом потоке smile 
просто не юзаю ProgressChanged event (потому как я - "старообрядец" )  smile и не люблю ProgressChangedEventHandler.

С воркером не разгуляешься. Приходится за каждым чихом менять код менять то в бизнес-слое, то слое представления... Это плохо... Потому как расчитан он на выполнение отвязанных от слоя представления (UI) узких задач, а для связи - немного в нем заложено... ProgressChanged event да его аргумент. 

Да и вопрос стоял так, чтобы обратиться к контролу из потока воркера. "Обратится" - понятие растяжимое, сегодня значит одно, а завтра может другое. Может завтра данные и не нужно будет передавать, может контролы дизейблить или даже новые создавать завтра нужно будет. К чему тогда ProgressChanged пришивать будем?

Я - сторонник простых потоков и инвоков. 

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