О потоковой обработке Winform в пользовательском интерфейсе C#

#c# #winforms

#c# #winforms

Вопрос:

Когда я использую метод делегирования для запуска моего метода, чтобы позволить обновиться пользовательскому интерфейсу, пользовательский интерфейс не обновляется. Я хочу остановить зависание пользовательского интерфейса во время выполнения метода, что я могу сделать?

     private void SearchToolStripButton_Click(object sender, EventArgs e)
    {
        Thread t = new Thread(new ThreadStart(StartMethod));
        t.Start();
    }
    private delegate void InvokeDelegate();
    public void StartMethod()
    {
        this.BeginInvoke(new InvokeDelegate(SearchData));
    }

    public void SearchData()
    {

        if (searchKeywordTextBox.Text == "")
        {
            MessageBox.Show("Please type the keyword!");
        }
        else
        {
            if (searchDateTimePicker.Checked == true)
            {
                switch (selectRangeComboBox.Text)
                {
                    case "Day": showDataToresultDataGridViewOnDay(searchKeywordTextBox.Text);
                        break;
                    case "Month": showDataToresultDataGridViewInMonth(searchKeywordTextBox.Text);
                        break;
                    case "Year": showDataToresultDataGridViewInYear(searchKeywordTextBox.Text);
                        break;
                    default: MessageBox.Show("Please select a Section");
                        break;
                }
            }
            else
            {
                showDataToresultDataGridView(searchKeywordTextBox.Text);
            }

        }
    }
    public void showDataToresultDataGridViewOnDay(string keyword)
    {
        DataGridView dayGrid = resultDataGridView;
        ShowResultDay day = new ShowResultDay();
        resultDataGridView.DataSource = day.ShowGridDay(searchDateTimePicker.Value.Day, searchDateTimePicker.Value.Month, searchDateTimePicker.Value.Year, keyword);
        resultLabel.Text = "Showing "   resultDataGridView.RowCount   " records in "   searchDateTimePicker.Value.Day   "/"   searchDateTimePicker.Value.Month   "/"   searchDateTimePicker.Value.Year;
    }
    public void showDataToresultDataGridViewInMonth(string keyword)
    {
        DataGridView monthGrid = resultDataGridView;
        ShowResultMonth month = new ShowResultMonth();
        resultDataGridView.DataSource = month.ShowGridMonth(searchDateTimePicker.Value.Month, searchDateTimePicker.Value.Year, keyword);
        resultLabel.Text = "Showing "   resultDataGridView.RowCount   " records in "   searchDateTimePicker.Value.Month   "/"   searchDateTimePicker.Value.Year;
    }
    public void showDataToresultDataGridViewInYear(string keyword)
    {
        DataGridView yearGrid = resultDataGridView;
        ShowResultYear year = new ShowResultYear();
        resultDataGridView.DataSource = year.ShowGridYear(searchDateTimePicker.Value.Year, keyword);
        resultLabel.Text = "Showing "   resultDataGridView.RowCount   " records in "   searchDateTimePicker.Value.Year;
    }
    public void showDataToresultDataGridView(string keyword)
    {
        ShowAllData all = new ShowAllData();
        var results = all.ShowGirdAll(keyword);
        resultDataGridView.DataSource = results;
        resultLabel.Text = "Showing "   resultDataGridView.RowCount   " records";
    }
  

Комментарии:

1. Вы понимаете, как работает BeginInvoke? Бесполезно вызывать StartMethod из фонового потока, если все, что он делает, — это планирует обратный вызов SearchData в потоке пользовательского интерфейса. Даже если вы создали фоновый поток, это бессмысленно, потому что вы все еще выполняете всю работу в потоке пользовательского интерфейса. Независимо от того, как вы его разделите, вам нужно будет отделить код пользовательского интерфейса от фонового кода, если вы хотите, чтобы они выполнялись в разных потоках.

Ответ №1:

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

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

Пример использования фонового рабочего:

 // Initialization
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork  = new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted  = new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

// Start elaboration
bw.RunWorkerAsync(objectArgument);

void bw_DoWork(object sender, DoWorkEventArgs e)
{
   // do your work (we are in the background-thread)
   // when you have finished, set your results in the e.Result property
   // N.B. don't show anything because we are in the background-thread
}

void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
   // here we have finished the work (we are in the UI-thread)
   // result is set in e.Result property
   // N.B. check e.Error property before to get e.Result because
   //      if there's an error e.Result throws an exception
}
  

Ответ №2:

В отличие от встроенного Win32 API или MFC, платформа Dot-net Framework не поддерживает многопоточные вызовы метода пользовательского интерфейса * для элементов управления . Таким образом, вы не можете создать элемент управления пользовательского интерфейса (window) ни в одном потоке и вызвать его метод и задать свойство из другого! (*здесь и ниже — методы пользовательского интерфейса, которые обновляют) Контекст пользовательского интерфейса. Вы можете считывать данные из элемента управления и не можете записывать в) Кстати, в родной среде такие вызовы тоже работают ненадежно, иногда вызывая плохо управляемое исключение … Но управляемая среда не может разрешить, чтобы вызов метода вообще вызывал исключение! Он просто «замораживает» такой вызов и ничего не делает для обновления. Кстати, эта функция платформы предназначена не только для фоновых потоков . Независимо от того, вызываете ли вы BeginInvoke (из встроенного пула потоков) или создаете пользовательский нефоновый поток другим способом, вы столкнетесь с той же проблемой. В вашем конкретном случае я не вижу никаких причин для того, чтобы два или более потоков совместно использовали один и тот же элемент управления — это также опасно с точки зрения безопасности данных. Как правило, другие потоки (и вообще параллелизм) используются для совершенно других целей: для любых длительных вычислений, операций ввода-вывода баз данных, файлов, сети. Хотя, конечно, вы можете вызвать некоторые окна сообщений в другом потоке .