Pertanyaan Cara Bersih untuk Meminta Peristiwa Lintas-Benang


Saya menemukan bahwa. NET model acara sedemikian rupa sehingga saya sering akan membesarkan acara pada satu utas dan mendengarkannya di utas lain. Saya bertanya-tanya apa cara terbersih untuk marshal acara dari thread latar belakang ke thread UI saya.

Berdasarkan saran komunitas, saya telah menggunakan ini:

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

75
2017-08-22 13:34


asal


Jawaban:


Beberapa pengamatan:

  • Jangan membuat delegasi sederhana secara eksplisit dalam kode seperti itu kecuali Anda pra-2.0 sehingga Anda dapat menggunakan:
   BeginInvoke(new EventHandler<CoolObjectEventArgs>(mCoolObject_CoolEvent), 
               sender, 
               args);
  • Juga Anda tidak perlu membuat dan mengisi array objek karena parameter args adalah jenis "params" sehingga Anda hanya bisa lulus dalam daftar.

  • Saya mungkin akan menyukainya Invoke lebih BeginInvoke karena yang terakhir akan menghasilkan kode yang dipanggil secara asinkron yang mungkin atau mungkin bukan yang Anda cari tetapi akan membuat penanganan pengecualian berikutnya sulit untuk menyebar tanpa panggilan ke EndInvoke. Apa yang akan terjadi adalah aplikasi Anda akan mendapatkan TargetInvocationException sebagai gantinya.


25
2017-08-22 13:45



saya sudah beberapa kode untuk ini on line. Ini jauh lebih baik daripada saran lainnya; pasti memeriksanya.

Contoh penggunaan:

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

41
2017-11-03 11:30



Saya menghindari deklarasi delegasi yang berlebihan.

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
}

Untuk non-acara, Anda dapat menggunakan System.Windows.Forms.MethodInvoker mendelegasikan atau System.Action.

EDIT: Selain itu, setiap peristiwa memiliki yang sesuai EventHandler delegasi jadi tidak perlu sama sekali untuk mengulang satu.


11
2017-08-22 13:42



Saya pikir cara terbersih adalah pastinya untuk pergi ke rute AOP. Buat beberapa aspek, tambahkan atribut yang diperlukan, dan Anda tidak perlu memeriksa lagi afinitas utas.


3
2018-04-29 17:33



Saya membuat kelas ajar lintas thread 'universal' berikut untuk tujuan saya sendiri, tapi saya rasa layak untuk membagikannya:

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

Dan Anda bisa menggunakan SetAnyProperty () dari utas lain:

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

Dalam contoh ini kelas KvaserCanReader di atas menjalankan utasnya sendiri dan membuat panggilan untuk mengatur properti teks label lb_Speed ​​pada formulir utama.


3
2017-10-05 09:36



Gunakan konteks sinkronisasi jika Anda ingin mengirim hasil ke utas UI. Saya perlu mengubah prioritas utas jadi saya mengubah dari menggunakan utas ulir utas (dikomentari kode) dan membuat utas baru saya sendiri. Saya masih bisa menggunakan konteks sinkronisasi untuk mengembalikan apakah database membatalkan berhasil atau tidak.

    #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());

        }
    }

3
2017-07-07 08:20



Sebagai catatan samping yang menarik, pengikatan WPF menangani marshaling secara otomatis sehingga Anda dapat mengikat UI ke properti objek yang dimodifikasi pada utas latar tanpa harus melakukan sesuatu yang istimewa. Ini telah terbukti sebagai penghemat waktu yang luar biasa bagi saya.

Di XAML:

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

2
2017-08-22 14:20



Saya selalu bertanya-tanya seberapa besar biayanya selalu asumsikan bahwa permintaan diperlukan ...

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

1
2017-08-22 13:44



Anda dapat mencoba mengembangkan semacam komponen generik yang menerima a SinkronisasiKonteks sebagai masukan dan menggunakannya untuk menjalankan peristiwa.


0
2017-08-22 13:53