Member 12226114 Ответов: 3

Управление потоками в C#


У меня есть две кнопки startbtn и stopbtn, combobox и listbox. Элементы коллекции combobox являются " истинными "и"ложными". ниже приводится,

private void startbtn_Click(object sender, EventArgs e)
        {
            int i = 0;
            while (comboBox1.Text == "TRUE")
            {
                
                i = i + 1;
                listBox1.Items.Add(i.ToString());
            }
        }

private void stopbtn_Click(object sender, EventArgs e)
        {
            comboBox1.Text = "FALSE";
        }


Дело в том, что мы все знаем, что в то время как приведенный выше код выполняется
1. мы не можем изменить текст combobox с "TRUE" на " FALSE"
2. мы не можем нажать кнопку stopbtn, чтобы завершить процесс.
3. Listbox1 не будет обновляться в каждом цикле до тех пор, пока процесс не будет завершен (если я использую класс, справочный ).

как использовать потоковую обработку для преодоления этих 3-х проблем? нужно ли нам нанизывать каждый элемент управления и обработчики событий отдельно? я не совсем понимаю, как поток нескольких обработчиков событий. Я изучаю концепцию потоковой передачи для моего предстоящего проекта. Вот с чего я должен начать. Пожалуйста, помогите мне понять

Что я уже пробовал:

http://www.c-sharpcorner.com/article/introduction-to-multithreading-in-C-Sharp/

BillWoodruff

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

Member 12226114

Окей. Спасибо

3 Ответов

Рейтинг:
8

Foothill

Если вы хотите воспользоваться преимуществами лямбда-выражений, это также может сработать для вас.
Примечание: это было написано для использования с WPF

public bool Stop { get; set; }
private void startbtn_Click(object sender, EventArgs e)
{
  Thread worker = new Thread(() =>
   {
     int i = 0;

     while (!Stop)
     {
       Dispatcher.Invoke(() => listBox1.Items.Add(i.ToString()));
       ++i;
     }
   }
  );

  worker.Start();
}
private void stopbtn_Click(object sender, EventArgs e)
{
  Stop = true;
}


pdoxtader

Да, это сработает. Но вы переписали его так полностью, что если вы не понимаете, что такое лямбда-выражение, и не имеете представления о том, что делает диспетчер в WPF, вы просто не поймете его. И давайте будем честны здесь - этот парень не понимает основных понятий потоковой передачи и не знал, что вы можете обновлять элементы пользовательского интерфейса только из потока пользовательского интерфейса, прежде чем публиковать этот вопрос.

Держу пари, я мог бы найти способ бросить туда троичное выражение, чтобы действительно сбить его с толку... Лол. Но, может быть, вместо этого кто-то из нас действительно должен объяснить это парню. Если никто не сжалится над ним, может быть, я сделаю это сам позже.

Member 12226114

Извините за задержку ответа. Я пробовал свое собственное решение. В конце концов, я понимаю, что элементы управления windows form должны быть вызваны для использования с потоком.

Member 12226114

Во всяком случае, я все еще не могу найти решение и чешу голову. Я попробую решение футхилла. Поскольку я уже выполнил эту задачу с помощью background worker,то теперь мне требовалось только потоковое выполнение.

pdoxtader

Я опубликовал решение и объяснение. надеюсь, это поможет.

- Пит

Member 12226114

Кроме того, мне было бы полезно, если бы вы, ребята, рассказали мне, как вызвать элементы управления winform в MYthread.

Foothill

Вы имеете в виду, как можно заставить элементы управления пользовательским интерфейсом изменять состояние рабочего потока? Если это так, то это может быть достигнуто с помощью различных методов синхронизации потоков.

Ссылка на MSDN: https://msdn.microsoft.com/en-us/library/mt679037.aspx

Синхронизация потоков может быть сложной задачей для поддержания, поскольку ОС определяет, когда потоку разрешено выполнять код, и вы никогда не можете гарантировать, что потоки будут выполняться в каком-либо определенном порядке. Я обнаружил, что проще всего использовать рабочие потоки с вызовами fire-and-forget, такими как обновление файлов и баз данных или подход fire-and-update-progress-UI-until-done с использованием функций обратного вызова, так что длительные задачи продолжают работать в фоновом режиме, но пользовательский интерфейс остается отзывчивым и постоянно информирует пользователя о том, что его запрос все еще обрабатывается, таким образом устраняя вопросы "почему ваше приложение блокируется".

Foothill

Да, это правда. Поскольку у ОП не было ответа на ответ Гриффа, я решил, что этого объяснения было достаточно. Мне захотелось добавить более продвинутую технику для сравнения.

Рейтинг:
21

pdoxtader

Вот решение с использованием Windows Forms. Я старался держаться как можно ближе к вашему коду, хотя решение Foothill, на мой взгляд, лучше, и вы могли бы использовать его, изменив одну строку кода:

Dispatcher.Invoke(() => listBox1.Items.Add(i.ToString()));


Должны быть изменены, чтобы:

this.Invoke((MethodInvoker)(() => listBox1.Items.Add(i.ToString())));


Да, мы используем лямбда-выражения. Если вы не знакомы с ними, вам следует прочитать о лямбда-выражениях и анонимных методах здесь, и здесь.

Вот мое решение (держась как можно ближе к вашему коду):
private void startbtn_Click(object sender, EventArgs e)
{
    String comboBox1Text = "";

    this.Invoke((MethodInvoker)(() => comboBox1Text = comboBox1.Text));
    new System.Threading.Thread(() =>
    {
        int i = 0;
        while (comboBox1Text.Equals("TRUE"))
        {
            i = i + 1;
            this.Invoke((MethodInvoker)(() => 
            { 
                listBox1.Items.Add(i.ToString());
                comboBox1Text = comboBox1.Text;
            }));
        }
    }).Start();
}

private void stopbtn_Click(object sender, EventArgs e)
{
    comboBox1.Text = "FALSE";
}


Это работает - я проверил. Позвольте мне немного объяснить, почему это работает.

Как сказал Грифф выше, вы можете обновлять элементы пользовательского интерфейса (UI) только из потока, который их создал. В Windows forms все элементы управления "знают", какой поток их создал, и даже форма является элементом управления. Вы можете спросить элемент управления, обращаетесь ли вы к нему из потока, который его создал, используя control.invokeRequired (возвращает bool). Если он возвращает true, то вам нужно "вызвать" элемент управления или запланировать код для запуска в потоке пользовательского интерфейса. Вы делаете это, вызывая управление.Взывать(). Найдите минутку, и прочитать о том, что контроль.вызывать ли здесь.

Существует много различных способов использования control. invoke (), но тот, который я использовал в этом примере - хотя, возможно, и не самый лучший (обычно я настраиваю вспомогательную функцию для обработки обновлений пользовательского интерфейса и передаю ей код в лямбда-выражении. Это делает ваш код более четким / читабельным), работает в этом примере.

Если я что-то пропустил или у вас есть какие-то вопросы,пожалуйста, не стесняйтесь спрашивать.

- Пит


Foothill

Получил свой голос 5.

pdoxtader

Спасибо, парень.

Member 12226114

Большое спасибо за ваше решение и объяснение. Получил разъяснение

Рейтинг:
1

OriginalGriff

Потоковую обработку организовать нетрудно, но этот код нуждается в модификации, потому что вы не можете получить доступ к элементам управления, кроме как в потоке, в котором они были созданы, - потоке пользовательского интерфейса. Таким образом, ваш тест завершения цикла / выхода потока не может напрямую включать combobox. К сожалению, это также относится и к списку - он должен быть вызван в поток пользовательского интерфейса, чтобы добавить ваши элементы.
Сказав это, сделать это нетрудно:

private bool endTheLoop = false;
private void startbtn_Click(object sender, EventArgs e)
    {
    endTheLoop = false;
    BackgroundWorker work = new BackgroundWorker();
    work.DoWork += work_DoWork;
    work.RunWorkerAsync();
    }

void work_DoWork(object sender, DoWorkEventArgs e)
    {
    int i = 0;
    while (!endTheLoop)
        {

        i = i + 1;
        listBox1.Invoke((MethodInvoker)delegate { listBox1.Items.Add(i.ToString()); });
        }

    }
private void stopbtn_Click(object sender, EventArgs e)
    {
    endTheLoop = true;
    }