2009-09-14 3 views
10

У меня есть функцияThread Control.Invoke

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. 

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

ответ

48

Есть два золотых правила многопоточности в Windows Forms:

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

Для того, чтобы взаимодействовать с UI из другого потока, вам нужен «маршал» звонок к потоку пользовательского интерфейса, используя делегата и вызывая Control.Invoke/BeginInvoke. You может проверить, нужно ли вам звонить 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, захватив любые переменные, которые вам нужны для доступа в пределах делегата.

См. my threading tutorial или Joe Albahari's для получения более подробной информации.

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

6

Взаимодействие на контроль в другой (Ui) нити должны быть вызваны следующим образом:

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; 
    } 
} 
+3

Хорошее решение; byt Я бы рекомендовал использовать «Action » из рамки, а не катиться (и поддерживать) свой. –

+0

Yep .. это в значительной степени стандартный способ сделать это. Причина, по которой это необходимо, заключается в том, что textBox1.Text может быть изменен только в потоке, в котором было создано текстовое поле - вызов - это процесс, используемый для возврата к этому потоку. –

+0

@Fredrik: Вы правы, но этот пример чаще встречается, во многом потому, что он был вокруг дольше. Любой из них будет работать. –

3

Я всегда находил this article полезной для данной конкретной проблемы.

В вашем примере вы пытаетесь изменить различные элементы управления из потока, который не создал элемент управления. Чтобы обойти эту проблему, при данном примере, вместо того, чтобы сделать это (при условии, что метод 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; 
    }); 
} 

Просто чтобы подчеркнуть тарелочки сделали @ Джон, я закомментирована вызов прервать поток. Поток закончится сам по себе. Поэтому нет причин прервать его таким образом.

+1

Ты лучший Мэтт. Спасибо. Это хорошо работает. – atromgame

+0

С тех пор, как он лучший, я дал ему верх, который никто из меня не имел (намек, намек). –

0

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

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 = ""; 
} 
} 
Смежные вопросы