none
Обработка интерфейса в отдельном потоке. RRS feed

  • Вопрос

  • Добрый вечер.

    У меня возникла необходимость изменить параметры компоненты во время выполнения программы  в отдельном потоке. Вот как я это делаю

    private void simpleButton5_Click(object sender, EventArgs e)
            {
                var line = this.barStaticItem1.Caption;
                new Thread(delegate() { DoWork(line); }).Start();
    //тут происходит к процесс классификации который может занимать несколько минут
    }
            void DoWork(string lines)
            {
                
                lines = "Классификация...";
                
                this.Invoke(
                    new MethodInvoker(() =>
                    {
                        this.barStaticItem1.Caption = lines;
                    })
                );
            }

    но при выполнении происходит следующее: свойство Caption применятется только по завершению  основного потока. А мне надо что бы надпись "Классификация" появился только пока выполняется код обозначенный комментарием. Как это можно реализовать?
    25 февраля 2012 г. 15:26

Ответы

  • Все я все сделал, я использовал BackgroundWorker но при этом возникла та же ошибка:

    System.InvalidOperationException: Cross thread operation detected.

    Что бы ее избежать перед запуском worker'а необходимо было написать:

    DevExpress.Data.CurrencyDataController.DisableThreadingProblemsDetection = true;

    Всем спасибо за помощь.
    • Помечено в качестве ответа mr_arti 8 марта 2012 г. 11:00
    8 марта 2012 г. 10:59

Все ответы

  • Чтобы так реализовать - тебе не нужен дополнительный поток.

    Все делать в одном потоке последовательно: поменял Caption => выполнил классификацию => поменял обратно.

    Зачем ты вообще поток вводил? Чтобы UI не блокировалось? Смена Caption'а в отдельном потоке не самая лучшая идея. По-моему, лучше "процесс классификации" вынести в отдельный поток...

    25 февраля 2012 г. 17:51
    Модератор
  • Да, не хочу что бы интерфейс блокировался. А как тогда сделать что бы данные из основного потока были доступны в созданном?
    25 февраля 2012 г. 17:57
  • > А как тогда сделать что бы данные из основного потока были доступны в созданном?


    using System;
    using System.Threading;
    using System.Threading.Tasks;
    ...
    
    void Start_Click(object sender, EventArgs e)
    {
        var mydata = new[] { 1, 2, 3 };
        Task.Factory.StartNew(data => DoWork(data), mydata);
        // или так  
        Task.Factory.StartNew(DoWork, mydata);
        // или так
        ThreadPool.QueueUserWorkItem(DoWork, mydata);
    }
    void DoWork(object state) // выполняется в отдельном потоке
    {
        var data = state as int[];
        ...
    
     
     
    • Помечено в качестве ответа Abolmasov Dmitry 29 февраля 2012 г. 13:05
    • Снята пометка об ответе mr_arti 5 марта 2012 г. 23:02
    25 февраля 2012 г. 20:30
  • Чтобы передать какие-либо локальные данные, т.е. объявленные внутри функции, то можете использовать параметрический запуск нового потока - ParameterizedThreadStart.

    Если же данные являются полями класса, т.е. "глобальными", то они и так будут доступны в потоке, объявленном в этом же классе.


    Для связи [mail]

    • Предложено в качестве ответа PashaPash 27 февраля 2012 г. 10:49
    27 февраля 2012 г. 7:35
  • А как сделать так что бы созданный поток после завершения возвращал dataset

    я делаю так:

    DataSet dataset1 = new DataSet(); 
    private void simpleButton5_Click(object sender, EventArgs e)
            {Thread thread = new Thread(Calc);
                    thread.Start();
                    thread.Join();
    gridControl3.DataSource = dataset1.Tables[0];
                    gridControl3.ForceInitialize();
    }
     public void Calc()
            {//...вычисления в итоге получается две таблици связанные в dataset и его надо вернуть.
    dataset1.Tables.Add(dt_etalon);
                dataset1.Tables.Add(dt_test);
    
                DataColumn keycolumn = dataset1.Tables[0].Columns["id"];
                DataColumn foreignKeyColumn = dataset1.Tables[1].Columns["id"];
    
                dataset1.Relations.Add("Классификация:", keycolumn, foreignKeyColumn);
    }

    Но при выполнении всплывает исключение

    System.InvalidOperationException: Cross thread operation detected.

    и еще если написать:

    thread.Join();
    

    будет ли форма отзываться на события типа перетаскивания.
    5 марта 2012 г. 19:54
  • Если вы хотите выполнять какие-либо действия с элементам управления, то такие действия необходимо выполнять в главном потоке. Это можно сделать вызвав метод Invoke у элемента управления.

    Для связи [mail]

    5 марта 2012 г. 22:26
  • Простите, но я хотел сказать что Сalc выполняется в новом потоке, а главный поток ожидает завершения работы созданного потока thread, а потом в gridview выводит dataset который создан в потоке thread. Я провери код, в потоке thread никаких действий с элементами интерфейса не происходит.
    5 марта 2012 г. 22:37
  • На какой строчке возникает исключение, привязан ли gridview к dataset на момент изменения этого dataset в другом потоке?

    По поводу join - основной поток, т.к. UI поток, будет остановлен и ждать завершения 2го потока, поэтому форма перетаскиваться может и будет, но вот элементы управления не будут отзывчивы.


    Для связи [mail]

    6 марта 2012 г. 7:46
  • Нет не привязан.

    ошибка возникает в файле Program.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows.Forms;
    
    namespace NeXT
    {
        static class Program
        {
            /// <summary>
            /// Главная точка входа для приложения.
            /// </summary>
            [STAThread]
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new XtraMain());// вот тут возникает ошибка         
        }
        }
    }
    
    Причем мне не понятно почему после завершения работы потока thread начинает выполнятся код в Program.cs.
    6 марта 2012 г. 11:53
  • На форумах DevExpress говорят, что данное исключение возникает, если вы изменяете источник данных из другого потока, скорей всего у вас так и есть, просто неявно. Попробуйте перед изменением отвязать его, т.е. DataSource = null. Также попробуйте вынести код изменения самого DataSet-а в главный поток с помощью BeginInvoke.

    Надеюсь это поможет.

    Также можете посмотреть темы на форуме DevExpress - What can cause the "Cross thread operation detected" error?  и cross thread operation detected


    Для связи [mail]

    6 марта 2012 г. 12:21
  • Пробовал DataSource = null не помогло, и реально этот грид не используется больше нигде, и это dataset тоже.

    Попробовал BeginInvoke:

    public delegate void MyDelegate();
            public void DelegateMethod()
            {
                dataset1.Tables.Add(dt_etalon);
                dataset1.Tables.Add(dt_test);
    
                DataColumn keycolumn = dataset1.Tables[0].Columns["id"];
                DataColumn foreignKeyColumn = dataset1.Tables[1].Columns["id"];
    
                dataset1.Relations.Add("Классификация:", keycolumn, foreignKeyColumn);
    
                gridControl3.DataSource = dataset1.Tables[0];
                gridControl3.ForceInitialize();
            }
    //и в созданном потоке написал:
    gridControl3.BeginInvoke(new MyDelegate(DelegateMethod));

    вернуло тоже исключение. (может быть я не правильно написал, никогда раньше не работал с BeginInvoke и вообще с потоками)

    если же вынести эти две строки из делегата в главный поток то тогда ошибка что таблица с индексом 0 не найдена:

                gridControl3.DataSource = dataset1.Tables[0];
                gridControl3.ForceInitialize();

    Может быть есть другой способ избежать зависания главного окна программы при длительных вычислениях, с возможностью в будующем может быть вывести безконечный прогресс бар.
    6 марта 2012 г. 13:21
  • Все я все сделал, я использовал BackgroundWorker но при этом возникла та же ошибка:

    System.InvalidOperationException: Cross thread operation detected.

    Что бы ее избежать перед запуском worker'а необходимо было написать:

    DevExpress.Data.CurrencyDataController.DisableThreadingProblemsDetection = true;

    Всем спасибо за помощь.
    • Помечено в качестве ответа mr_arti 8 марта 2012 г. 11:00
    8 марта 2012 г. 10:59
  • Таким образом вы просто скрыли предупреждение об ошибке, сама ошибка может возникнуть при стечении обстоятельств. Но это может быть достаточно редко.


    Для связи [mail]

    11 марта 2012 г. 6:11