Сведения о вопросе

Junior

18:15, 28th August, 2020

Теги

c#   multithreading   events    

Самый чистый способ вызова событий перекрестного потока

Просмотров: 497   Ответов: 9

Я нахожу, что модель событий .NET такова, что я часто вызываю событие в одном потоке и слушаю его в другом потоке. Мне было интересно, какой самый чистый способ маршалировать событие из фонового потока в мой поток UI.

Основываясь на предложениях сообщества, я использовал это:

// earlier in the code
mCoolObject.CoolEvent+= 
           new CoolObjectEventHandler(mCoolObject_CoolEvent);
// then
private void mCoolObject_CoolEvent(object sender, CoolObjectEventArgs args)
{
    if (InvokeRequired)
    {
        CoolObjectEventHandler cb =
            new CoolObjectEventHandler(
                mCoolObject_CoolEvent);
        Invoke(cb, new object[] { sender, args });
        return;
    }
    // do the dirty work of my method here
}



  Сведения об ответе

SKY

01:41, 11th August, 2020

У меня есть код для этого в интернете. Это гораздо лучше, чем другие предложения; определенно проверьте это.

Пример использования:

private void mCoolObject_CoolEvent(object sender, CoolObjectEventArgs args)
{
    // You could use "() =>" in place of "delegate"; it's a style choice.
    this.Invoke(delegate
    {
        // Do the dirty work of my method here.
    });
}


  Сведения об ответе

dumai

02:47, 25th August, 2020

Несколько наблюдений:

  • Не создавайте простые делегаты явно в таком коде, если только вы не являетесь pre-2.0, чтобы вы могли использовать:
   BeginInvoke(new EventHandler<CoolObjectEventArgs>(mCoolObject_CoolEvent), 
               sender, 
               args);
  • Кроме того, вам не нужно создавать и заполнять массив объектов, потому что параметр args имеет тип "params", поэтому вы можете просто передать его в списке.

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


  Сведения об ответе

PROGA

16:07, 12th August, 2020

Я избегаю избыточных деклараций делегатов.

private void mCoolObject_CoolEvent(object sender, CoolObjectEventArgs args)
{
    if (InvokeRequired)
    {
        Invoke(new Action<object, CoolObjectEventArgs>(mCoolObject_CoolEvent), sender, args);
        return;
    }
    // do the dirty work of my method here
}

Для не-событий можно использовать делегат System.Windows.Forms.MethodInvoker или System.Action .

EDIT: кроме того, каждое событие имеет соответствующий делегат EventHandler , поэтому нет никакой необходимости повторно объявлять его.


  Сведения об ответе

crush

22:01, 27th August, 2020

Я сделал следующий класс 'universal' cross thread call для своей собственной цели, но я думаю, что стоит поделиться им:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace CrossThreadCalls
{
  public static class clsCrossThreadCalls
  {
    private delegate void SetAnyPropertyCallBack(Control c, string Property, object Value);
    public static void SetAnyProperty(Control c, string Property, object Value)
    {
      if (c.GetType().GetProperty(Property) != null)
      {
        //The given property exists
        if (c.InvokeRequired)
        {
          SetAnyPropertyCallBack d = new SetAnyPropertyCallBack(SetAnyProperty);
          c.BeginInvoke(d, c, Property, Value);
        }
        else
        {
          c.GetType().GetProperty(Property).SetValue(c, Value, null);
        }
      }
    }

    private delegate void SetTextPropertyCallBack(Control c, string Value);
    public static void SetTextProperty(Control c, string Value)
    {
      if (c.InvokeRequired)
      {
        SetTextPropertyCallBack d = new SetTextPropertyCallBack(SetTextProperty);
        c.BeginInvoke(d, c, Value);
      }
      else
      {
        c.Text = Value;
      }
    }
  }

И вы можете просто использовать SetAnyProperty() из другого потока:

CrossThreadCalls.clsCrossThreadCalls.SetAnyProperty(lb_Speed, "Text", KvaserCanReader.GetSpeed.ToString());

В этом примере приведенный выше класс KvaserCanReader запускает свой собственный поток и выполняет вызов для установки свойства text метки lb_Speed в главной форме.


  Сведения об ответе

KOMP

13:00, 25th August, 2020

Я думаю, что самый чистый способ-это определенно пойти по маршруту AOP. Сделайте несколько аспектов, добавьте необходимые атрибуты, и вам больше никогда не придется проверять сходство потоков.


  Сведения об ответе

lourence

06:19, 18th August, 2020

Используйте контекст синхронизации, если вы хотите отправить результат в поток UI. Мне нужно было изменить приоритет потока, поэтому я отказался от использования потоков пула потоков (закомментированный код) и создал новый собственный поток. Я все еще мог использовать контекст синхронизации, чтобы вернуть, удалось ли отменить отмену базы данных или нет.

    #region SyncContextCancel

    private SynchronizationContext _syncContextCancel;

    /// <summary>
    /// Gets the synchronization context used for UI-related operations.
    /// </summary>
    /// <value>The synchronization context.</value>
    protected SynchronizationContext SyncContextCancel
    {
        get { return _syncContextCancel; }
    }

    #endregion //SyncContextCancel

    public void CancelCurrentDbCommand()
    {
        _syncContextCancel = SynchronizationContext.Current;

        //ThreadPool.QueueUserWorkItem(CancelWork, null);

        Thread worker = new Thread(new ThreadStart(CancelWork));
        worker.Priority = ThreadPriority.Highest;
        worker.Start();
    }

    SQLiteConnection _connection;
    private void CancelWork()//object state
    {
        bool success = false;

        try
        {
            if (_connection != null)
            {
                log.Debug("call cancel");
                _connection.Cancel();
                log.Debug("cancel complete");
                _connection.Close();
                log.Debug("close complete");
                success = true;
                log.Debug("long running query cancelled" + DateTime.Now.ToLongTimeString());
            }
        }
        catch (Exception ex)
        {
            log.Error(ex.Message, ex);
        }

        SyncContextCancel.Send(CancelCompleted, new object[] { success });
    }

    public void CancelCompleted(object state)
    {
        object[] args = (object[])state;
        bool success = (bool)args[0];

        if (success)
        {
            log.Debug("long running query cancelled" + DateTime.Now.ToLongTimeString());

        }
    }


  Сведения об ответе

baggs

22:51, 11th August, 2020

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

private void OnCoolEvent(CoolObjectEventArgs e)
{
  BeginInvoke((o,e) => /*do work here*/,this, e);
}


  Сведения об ответе

LIZA

14:56, 10th August, 2020

Интересно отметить, что привязка WPF обрабатывает маршалинг автоматически, так что вы можете привязать UI к свойствам объекта, которые изменяются в фоновых потоках, не делая ничего особенного. Это оказалось для меня большим спасением времени.

В XAML году:

<TextBox Text="{Binding Path=Name}"/>


  Сведения об ответе

prince

11:05, 5th August, 2020

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться