Как я превращаю обратные вызовы события в свои ориентированные на многопотоковое исполнение формы победы?

Как будто вы пытаетесь получить доступ к объекту, который является null. Рассмотрим ниже пример:

TypeA objA;

. В это время вы только что объявили этот объект, но не инициализировали или не инициализировали. И всякий раз, когда вы пытаетесь получить доступ к каким-либо свойствам или методам в нем, он будет генерировать NullPointerException, что имеет смысл.

См. Также этот пример:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
38
задан Ijas Ameenudeen 20 January 2019 в 13:58
поделиться

5 ответов

Для упрощения кода Simon немного Вы могли использовать созданный в универсальном делегате Действия. Это сохраняет усыпание Вашего кода с набором типов делегата, в которых Вы действительно не нуждаетесь. Кроме того, в.NET 3.5 они добавили параметр параметрических усилителей к Вызвать методу, таким образом, Вы не должны определять временный массив.

void SomethingHappened(object sender, EventArgs ea)
{
   if (InvokeRequired)
   {
      Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea);
      return;
   }

   textBox1.Text = "Something happened";
}
34
ответ дан Jake Pearson 27 November 2019 в 03:40
поделиться

Вот угловые точки:

  1. Вы не можете выполнить вызовы управления UI от различного потока, чем тот, который они были созданы на (поток формы).
  2. вызовы Делегата (т.е., рычаги события) инициированы на том же потоке как объект, который запускает событие.

Так, если Вы имеете отдельный поток "механизма", делающий некоторую работу, и имеете некоторый UI, наблюдающий за изменениями состояния, которые могут быть отражены в UI (таком как индикатор выполнения или безотносительно), у Вас есть проблема. Огонь механизма объект изменил событие, которое было сцеплено Формой. Но делегат обратного вызова, что к Форме, зарегистрированной в механизме, обращаются thread†механизма ¦ не на потоке Формы. И таким образом, Вы не можете обновить средства управления от того обратного вызова. Doh!

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

private delegate void EventArgsDelegate(object sender, EventArgs ea);

void SomethingHappened(object sender, EventArgs ea)
{
   //
   // Make sure this callback is on the correct thread
   //
   if (this.InvokeRequired)
   {
      this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });
      return;
   }

   //
   // Do something with the event such as update a control
   //
   textBox1.Text = "Something happened";
}

Это довольно просто действительно.

  1. Использование InvokeRequired, чтобы узнать, произошел ли этот обратный вызов на корректном потоке.
  2. В противном случае тогда повторно вызывают обратный вызов на корректный поток с теми же параметрами. Можно повторно вызвать метод при помощи эти , Вызывают (блокирование) или BeginInvoke (неблокирование) методы.
  3. В следующий раз, когда функция вызвана, , InvokeRequired возвращает false, потому что мы находимся теперь на корректном потоке, и все счастливы.

Это - очень компактный способ решить эту проблему и сделать Ваши Формы безопасными от многопоточных обратных вызовов события.

17
ответ дан Simon Gillbee 27 November 2019 в 03:40
поделиться

Я использую анонимные методы много в этом сценарии:

void SomethingHappened(object sender, EventArgs ea)
{
   MethodInvoker del = delegate{ textBox1.Text = "Something happened"; }; 
   InvokeRequired ? Invoke( del ) : del(); 
}
9
ответ дан Jason Diller 27 November 2019 в 03:40
поделиться

Я немного опаздываю к этой теме, но Вы могли бы хотеть смотреть на Основанный на событии Асинхронный Шаблон . Когда реализовано правильно, это гарантирует, что события всегда генерируются от потока UI.

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

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public class MainForm : Form
    {
        private TypeWithAsync _type;

        [STAThread()]
        public static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(new MainForm());
        }

        public MainForm()
        {
            _type = new TypeWithAsync();
            _type.DoSomethingCompleted += DoSomethingCompleted;

            var panel = new FlowLayoutPanel() { Dock = DockStyle.Fill };

            var btn = new Button() { Text = "Synchronous" };
            btn.Click += SyncClick;
            panel.Controls.Add(btn);

            btn = new Button { Text = "Asynchronous" };
            btn.Click += AsyncClick;
            panel.Controls.Add(btn);

            Controls.Add(panel);
        }

        private void SyncClick(object sender, EventArgs e)
        {
            int value = _type.DoSomething();
            MessageBox.Show(string.Format("DoSomething() returned {0}.", value));
        }

        private void AsyncClick(object sender, EventArgs e)
        {
            _type.DoSomethingAsync();
        }

        private void DoSomethingCompleted(object sender, DoSomethingCompletedEventArgs e)
        {
            MessageBox.Show(string.Format("DoSomethingAsync() returned {0}.", e.Value));
        }
    }

    class TypeWithAsync
    {
        private AsyncOperation _operation;

        // synchronous version of method
        public int DoSomething()
        {
            Thread.Sleep(5000);
            return 27;
        }

        // async version of method
        public void DoSomethingAsync()
        {
            if (_operation != null)
            {
                throw new InvalidOperationException("An async operation is already running.");
            }

            _operation = AsyncOperationManager.CreateOperation(null);
            ThreadPool.QueueUserWorkItem(DoSomethingAsyncCore);
        }

        // wrapper used by async method to call sync version of method, matches WaitCallback so it
        // can be queued by the thread pool
        private void DoSomethingAsyncCore(object state)
        {
            int returnValue = DoSomething();
            var e = new DoSomethingCompletedEventArgs(returnValue);
            _operation.PostOperationCompleted(RaiseDoSomethingCompleted, e);
        }

        // wrapper used so async method can raise the event; matches SendOrPostCallback
        private void RaiseDoSomethingCompleted(object args)
        {
            OnDoSomethingCompleted((DoSomethingCompletedEventArgs)args);
        }

        private void OnDoSomethingCompleted(DoSomethingCompletedEventArgs e)
        {
            var handler = DoSomethingCompleted;

            if (handler != null) { handler(this, e); }
        }

        public EventHandler<DoSomethingCompletedEventArgs> DoSomethingCompleted;
    }

    public class DoSomethingCompletedEventArgs : EventArgs
    {
        private int _value;

        public DoSomethingCompletedEventArgs(int value)
            : base()
        {
            _value = value;
        }

        public int Value
        {
            get { return _value; }
        }
    }
}
2
ответ дан OwenP 27 November 2019 в 03:40
поделиться

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

0
ответ дан Chris Farmer 27 November 2019 в 03:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: