Вызвать(Делегировать)


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

Invoke(Delegate):

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

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

9 73

9 ответов:

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

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

С управление.Свойство invokerequired

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

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

  • какое действие имеет приоритет?
  • возможно ли, чтобы это произошло одновременно?
  • что происходит со всеми другими командами, которые должен использовать графический интерфейс бежать?

фактически, вы вмешиваетесь в очередь, что может иметь множество непредвиденных последствий. Invoke-это фактически "вежливый" способ получить то, что вы хотите сделать в этой очереди, и это правило было применено с .Net 2.0 и далее через thrown InvalidOperationException.

чтобы понять, что на самом деле происходит за кулисами, и что подразумевается под "потоком GUI", полезно понять, что такое насос сообщений или цикл сообщений.

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

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

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

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

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

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

// 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.

существует также дальше напишите о том, что произошло исторически это может быть интересно.

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

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

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

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

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"));
}

Invoke ((MethodInvoker)delegate{ textBox1.Text = "Test";});

на практике это означает, что делегат гарантированно вызывается в главном потоке. Это важно, потому что в случае элементов управления 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)
}

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

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

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

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

может быть следующие строки имеют смысл для использования 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 текущий код выполняется в рабочем потоке, поэтому вызовите этот.Invoke (d, new object[] { text });

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

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

пример лямбда

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

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

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