it-swarm.com.ru

Вызов (делегат)

Кто-нибудь может объяснить, пожалуйста, это заявление, написанное на этой ссылка

Invoke(Delegate):

Выполняет указанный делегат в потоке, которому принадлежит базовый дескриптор окна элемента управления .

Кто-нибудь может объяснить, что это значит (особенно жирный) Я не могу получить это ясно

78
user1903439

Ответ на этот вопрос заключается в том, как работает C # Controls

Элементы управления в Windows Forms связаны с конкретным потоком и не являются Поток безопасно. Поэтому, если вы вызываете метод элемента управления из другой поток, вы должны использовать один из методов вызова элемента управления для Маршал вызов в нужную ветку. Это свойство может быть использовано для определить, нужно ли вызывать метод invoke, что может быть полезно, если Вы не знаете, какой поток владеет элементом управления.

От Control.InvokeRequired

По сути, Invoke обеспечивает, чтобы вызываемый вами код находился в потоке, на котором «живет» элемент управления, эффективно предотвращая перекрестные исключения.

С исторической точки зрения, в .Net 1.1 это было фактически разрешено. Это означает, что вы можете попытаться выполнить код в потоке «GUI» из любого фонового потока, и это в основном будет работать. Иногда это может привести к закрытию вашего приложения, потому что вы эффективно перехватываете поток GUI, пока он делал что-то еще. Это исключение Cross Threaded - представьте, что вы пытаетесь обновить TextBox, пока графический интерфейс рисует что-то другое. 

  • Какое действие имеет приоритет? 
  • Возможно ли и то и другое одновременно? 
  • Что происходит со всеми другими командами, необходимыми для выполнения графического интерфейса?

По сути, вы прерываете очередь, что может иметь множество непредвиденных последствий. Invoke, по сути, является «вежливым» способом получения того, что вы хотите сделать, в эту очередь, и это правило было применено начиная с .Net 2.0 через выброшенное InvalidOperationException .

Чтобы понять, что на самом деле происходит за кулисами, и что подразумевается под «потоком GUI», полезно понять, что такое Message Pump или Message Loop. 

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

Другое чтение, которое вы можете найти полезным, включает в себя:

Что случилось с Begin Invoke

Одним из основных правил программирования Windows GUI является то, что только Поток, создавший элемент управления, может получить доступ и/или изменить его содержимое (за исключением нескольких документированных исключений). Попробуйте сделать это из любого другого нить, и вы получите непредсказуемое поведение от тупика до исключения для половины обновленного интерфейса. Правильный путь, чтобы обновить управление из другого потока заключается в публикации соответствующего сообщения в очередь сообщений приложения. Когда сообщение насос добирается до после выполнения этого сообщения элемент управления будет обновлен в том же формате Поток, создавший его (помните, насос сообщений работает в главном потоке ).

и, для более подробного обзора кода с типичным примером:

Недопустимые операции с поперечными потоками

// the canonical form (C# consumer)

public delegate void ControlStringConsumer(Control control, string text);  // defines a delegate type

public void SetText(Control control, string text) {
    if (control.InvokeRequired) {
        control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text});  // invoking itself
    } else {
        control.Text=text;      // the "functional part", executing only on the main thread
    }
}

Как только вы оцените InvokeRequired, вы можете рассмотреть возможность использования метода расширения для упаковки этих вызовов. Это умело рассмотрено в вопросе переполнения стека Очистка кода, заполненного Invoke Required

Существует также дальнейшая запись того, что произошло исторически которая может представлять интерес.

115
dash

Элемент управления или окно в Windows Forms - это просто оболочка вокруг окна Win32, идентифицируемая handle (иногда называемая HWND). Большинство вещей, которые вы делаете с элементом управления, в конечном итоге приводят к вызову Win32 API, который использует этот дескриптор. Дескриптор принадлежит потоку, который его создал (как правило, основной поток), и не должен управляться другим потоком. Если по какой-то причине вам нужно что-то сделать с элементом управления из другого потока, вы можете использовать Invoke, чтобы попросить главный поток сделать это от вашего имени.

Например, если вы хотите изменить текст метки из рабочего потока, вы можете сделать что-то вроде этого:

theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
61
Thomas Levesque

Если вы хотите изменить элемент управления, это должно быть сделано в потоке, в котором он был создан. Этот метод Invoke позволяет вам выполнять методы в связанном потоке (потоке, которому принадлежит основной дескриптор окна элемента управления).

В приведенном ниже примере thread1 генерирует исключение, потому что SetText1 пытается изменить textBox1.Text из другого потока. Но в потоке 2 действие в SetText2 выполняется в потоке, в котором был создан TextBox

private void btn_Click(object sender, EvenetArgs e)
{
    var thread1 = new Thread(SetText1);
    var thread2 = new Thread(SetText2);
    thread1.Start();
    thread2.Start();
}

private void SetText1() 
{
    textBox1.Text = "Test";
}

private void SetText2() 
{
    textBox1.Invoke(new Action(() => textBox1.Text = "Test"));
}
20
Mehmet Ataş
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });
5
BestDeveloper

this.Invoke(delegate) убедитесь, что вы вызываете делегат аргумента для this.Invoke() в основном/созданном потоке. 

Я могу сказать, что правило Thumb не имеет доступа к элементам управления формы, кроме как из основного потока.

Может быть, следующие строки имеют смысл для использования Invoke ()

    private void SetText(string text)
    {
        // InvokeRequired required compares the thread ID of the
        // calling thread to the thread ID of the creating thread.
        // If these threads are different, it returns true.
        if (this.textBox1.InvokeRequired)
        {   
            SetTextCallback d = new SetTextCallback(SetText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.textBox1.Text = text;
        }
    }

Существуют ситуации, когда вы создаете поток Threadpool (т. Е. Рабочий поток), он будет работать в основном потоке. Это не создаст новый поток, потому что основной поток доступен для обработки дальнейших инструкций. Поэтому сначала выясните, является ли текущий запущенный поток основным потоком, используя this.InvokeRequired, если возвращает true, текущий код выполняется в рабочем потоке, поэтому вызовите this.Invoke (d, new object [] {text});

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

2
Srikanth

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

Шаблон является:

void OnEvent(object sender, EventArgs e)
{
   if (this.InvokeRequired)
   {
       this.Invoke(() => this.OnEvent(sender, e);
       return;
   }

   // do stuff (now you know you are on the main thread)
}
2
briantyler

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

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

1
Marc Gravell

Делегат по существу встроенный Action's или Func<T>. Вы можете объявить делегата вне области метода, который вы запускаете или используете выражение lambda (=>); поскольку вы запускаете делегат в методе, вы запускаете его в потоке, который запускается для текущего окна/приложения, которое выделено жирным шрифтом.

Лямбда-пример

int AddFiveToNumber(int number)
{
  var d = (int i => i + 5);
  d.Invoke(number);
}
0
LukeHennerley

Это означает, что передаваемый вами делегат выполняется в потоке, который создал объект Control (который является потоком пользовательского интерфейса).

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

0
user1610015