Управление потоками. Вызов - PullRequest
10 голосов
/ 14 сентября 2009

У меня есть функция

public void ShowAllFly()
{  
        cbFly.Items.Clear();
        cbFly.Items.Add("Uçuş Seçiniz...");

        dsFlyTableAdapters.tblFlyTableAdapter _t=new KTHY.dsFlyTableAdapters.tblFlyTableAdapter();
        dsFly _mds = new dsFly();
        _mds.EnforceConstraints = false;
        dsFly.tblFlyDataTable _m = _mds.tblFly;
        _t.Fill(_m);
        foreach (DataRow _row in _m.Rows)
        {
            cbFly.Items.Add(_row["FlyID"].ToString()+"-"+_row["FlyName"].ToString() + "-" + _row["FlyDirection"].ToString() + "-" + _row["FlyDateTime"].ToString());
        }
        _Thread.Abort();
        timer1.Enabled = false;
        WaitPanel.Visible = false;
}

В функции Form_Load Вот так;

{
    _Thread = new System.Threading.Thread(new System.Threading.ThreadStart(ShowAllFly));
    _Thread.Start();
    _Thread.Priority = System.Threading.ThreadPriority.Normal;
}

Но когда я ее запускаю;

в функции ShowAllFly

cbFly.Items.Clear(); ----  HERE Gives ERROR  LIKE  Control.Invoke must be used to interact with controls created on a separate thread.

В чем проблема?

Ответы [ 5 ]

49 голосов
/ 14 сентября 2009

В Windows Forms существует два золотых правила потоков:

  • Не трогайте никакие свойства или методы элемента управления (кроме тех, которые явно указаны как допустимые) из любого потока, кроме того, который создал «дескриптор» элемента управления (обычно это только один поток пользовательского интерфейса)
  • Не блокируйте поток пользовательского интерфейса в течение значительного промежутка времени, иначе приложение не будет отвечать на запросы

Чтобы взаимодействовать с пользовательским интерфейсом из другого потока, необходимо «маршалировать» вызов потока пользовательского интерфейса, используя делегат и вызывая Control.Invoke / BeginInvoke. Вы можете проверить, нужно ли вам звонить Invoke, используя свойство InvokeRequired, но в наши дни я лично все равно склонен делать это в любом случае - не нужно много штрафов за вызов, когда вам не нужно к.

Лямбда-выражения в C # 3 (или анонимные методы в C # 2) также делают это намного приятнее.

Например, вы можете использовать:

cbFly.Invoke((MethodInvoker)(() => cbFly.Items.Clear()));

Все скобки немного мешают, так что вы можете добавить метод расширения, подобный этому, если вы используете C # 3:

public static void Invoke(this Control control, MethodInvoker action)
{
    control.Invoke(action);
}

Тогда вы могли бы сделать:

cbFly.Invoke(() => cbFly.Items.Clear());

что намного проще. Обычно вы можете избежать использования MethodInvoker, захватив все переменные, к которым вам нужен доступ в делегате.

См. мой учебник потоков или Джо Албахари для более подробной информации.

В качестве вторичного вопроса я вижу, что вы используете Thread.Abort - фактически в своем собственном потоке, несмотря на то, что после него есть другие вызовы. Зачем? Отмена любого потока , отличного , кроме вашего, является вызовом типа «только для экстренных случаев» (за которым обычно следует загрузка приложения в любом случае), и я не вижу причин прерывать текущий поток, когда он еще работа, которую предстоит сделать потом ...

6 голосов
/ 14 сентября 2009

Взаимодействие с элементами управления в другом потоке (пользовательском интерфейсе) должно вызываться так:

public delegate void ProcessResultDelegate(string result);
void ProcessResult(string result)
{
    if (textBox1.InvokeRequired)
    {
        var d = new ProcessResultDelegate(ProcessResult);
        d.Invoke(result);
    }
    else
    {
        textBox1.Text = result;
    }
}
4 голосов
/ 15 сентября 2009

Я всегда находил эту статью полезной по этому конкретному вопросу.

В вашем примере вы пытаетесь изменить различные элементы управления из потока, который не создал элемент управления. Чтобы обойти эту проблему на примере, сделайте это вместо этого (при условии, что метод ShowAllFly () является методом в вашей форме):

public void ShowAllFly()
{
    Invoke((MethodsInvoker) delegate {
        cbFly.Items.Clear();
        cbFly.Items.Add("Uçuş Seçiniz...");
        dsFlyTableAdapters.tblFlyTableAdapter _t =
            new KTHY.dsFlyTableAdapters.tblFlyTableAdapter();
        dsFly _mds = new dsFly();
        _mds.EnforceConstraints = false;
        dsFly.tblFlyDataTable _m = _mds.tblFly;
        _t.Fill(_m);
        foreach (DataRow _row in _m.Rows)
        {
            cbFly.Items.Add(_row["FlyID"].ToString() + "-" +
                            _row["FlyName"].ToString() + "-" +
                            _row["FlyDirection"].ToString() + "-" +
                            _row["FlyDateTime"].ToString());
        }
        //_Thread.Abort(); // WHY ARE YOU TRYING TO DO THIS?
        timer1.Enabled = false;
        WaitPanel.Visible = false;
    } );
}

Просто чтобы подчеркнуть мысль, высказанную @Jon Skeet, я прокомментировал призыв прервать поток. Нить закончится сама по себе. Нет причин прерывать его таким образом.

1 голос
/ 03 декабря 2018

Это лучший способ работы с элементами управления в потоке.

Сначала вы должны использовать нить из однопоточной квартиры.

...
Thread th = new Thread(yourThreadStart);
            th.SetApartmentState(ApartmentState.STA);
th.Start();
...

Далее скопируйте этот метод между вашим кодом!

public static void SetControlThreadSafe(Control control, Action<object[]> action, object[] args)
{
      if (control.InvokeRequired)
            try { control.Invoke(new Action<Control, Action<object[]>, object[]>(SetControlThreadSafe), control, action, args); } catch { }
      else action(args);
}

Наконец, ваши изменения управления должны быть выполнены, как показано ниже:

...

    SetControlThreadSafe(textbox1, (arg) =>
          {
                textbox1.Text = "I`m Working in a Thread";
          }, null);
...

Наслаждайтесь ...

0 голосов
/ 31 января 2014

Это должен быть вызов ... Но вызов должен ждать, пока основной поток, я имею в виду, что вы не получите ошибку таким образом, но это не совсем параллельная работа, если вы хотите запустить более одного процесса одновременно, просто создайте более одного нить

Thread thread = new Thread(new delegate_method(method));//you must create delegate before
thread.start ();
Thread thread2 = new Thread(new delegate_method(method2));//you must create delegate before
thread.start ();

обрабатывать два процесса одновременно

    void method ()
{
//do something here -- working background Remember can not control any UI control from here
finish_thread()
}

void method2 ()
{
//do something here -- working background Remember can not control any UI control from here
finish_thread()
}

void finish_thread()
{
if(invoke.Required)
{
//Here you have to call delegate method here with UI
BeginInvoke(new delegate_method(finish_thread));
}
else
{
//Now you can control UI thread from here and also you finished background work
//Do something working with UI thread
textBox.Text = "";
}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...