【问题标题】:Execute a method in main thread from event handler从事件处理程序在主线程中执行方法
【发布时间】:2012-11-21 19:56:38
【问题描述】:

我有一个继承自 Queue 类的自定义 Queue 类。它有一个事件 ItemAdded。在此事件的事件处理程序中,我正在执行一个方法。但它在主线程之外运行,虽然我希望它在主线程中。我不知道该怎么做。有什么建议吗?

//My custom class


using System;
using System.Collections; //Required to inherit non-generic Queue class.

namespace QueueWithEvent
{
    public class SmartQueue:Queue
    {

        public delegate void ItemAddedEventHandler(object sender, EventArgs e);

        public event ItemAddedEventHandler ItemAdded;

        protected virtual void OnItemAdded(EventArgs e)
        {
           if (ItemAdded != null)
           {
              ItemAdded(this, e);
           }
    }

    public override void Enqueue(object Item)
    {
        base.Enqueue(Item);
        OnItemAdded(EventArgs.Empty);
    }        

   }
}



 //Winform application

 using System;
 using System.ComponentModel;
 using System.Windows.Forms;
 using QueueWithEvent;

 namespace TestApp
 {
    public partial class Form1 : Form
    {

    SmartQueue qTest = new SmartQueue();

    public Form1()
    {
        InitializeComponent();
        qTest.ItemAdded += new SmartQueue.ItemAddedEventHandler(this.QChanged);
    }

    private void btnStartBgw_Click(object sender, EventArgs e)
    {
        DisplayThreadName();
        bgwTest.RunWorkerAsync();
    }

    private void bgwTest_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            for (int i = 0; i < 11; i++)
            {
                string valueTExt = i.ToString();
                qTest.Enqueue(valueTExt);
                System.Threading.Thread.Sleep(5000);
            }
        }
        catch (Exception Ex)
        {
            MessageBox.Show(Ex.Message);

        }
    }


    private void DisplayThreadName()
    {
        string tName = System.Threading.Thread.CurrentThread.ManagedThreadId.ToString();
        txtThreadName.Text = tName;           
    }

    private void QChanged(object sender, EventArgs e)
    {
        //#########I want this method to run on main thread #############
        DisplayThreadName();
    }
}
}

提前致谢。 SK保罗。

【问题讨论】:

    标签: c# winforms multithreading events backgroundworker


    【解决方案1】:

    您正在后台线程上排队项目(DoWork 事件处理程序在后台线程上运行),因此您的事件也在后台线程中引发。

    使用InvokeRequired 方法来验证您是否在 UI 线程上。如果没有,则使用Invoke 在 UI 线程上运行代码:

     private void QChanged(object sender, EventArgs e)
     {
         if (InvokeRequired)
         {
             Invoke((MethodInvoker)delegate { QChanged(sender, e); });
             return;
         }
         // this code will run on main (UI) thread 
         DisplayThreadName();
     }
    

    您的另一个选择 - 使用 ProgressChanged 事件将项目排入队列(不要忘记将 WorkerReportsProgress 设置为 true)。此事件处理程序在 UI 线程上运行:

    private void bgwTest_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = (BackgroundWorker)sender;
    
        for (int i = 0; i < 11; i++)
        { 
            // use user state for passing data
            // which is not reflecting progress percentage
            worker.ReportProgress(0, i);
            System.Threading.Thread.Sleep(5000);
        }
    }
    
    private void bgwTest_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
         string valueTExt = e.UserState.ToString();
         qTest.Enqueue(valueTExt);
    }
    

    【讨论】:

    • 如果你的主线程没有Form怎么办?例如,托盘应用程序的 ApplicationContext 类。然后调用不可用。
    【解决方案2】:

    您可以使用与BackgroundWorker 相同的方法,即将AsyncOperation 合并为您的类中的成员,它可以将操作分派到创建它的线程。 p>

    protected AsyncOperation AsyncOp;
    

    在你的构造函数中用“null”参数实例化它。这会创建一个“绑定”到当前线程的异步操作。

    public SmartQueue()
    {
        AsyncOp = AsyncOperationManager.CreateOperation(null);
    }
    

    然后您可以使用 AsyncOp 来Post 您的活动。

    public override void Enqueue(object Item)
    {
        base.Enqueue(Item);
        AsyncOp.Post(OnItemAdded, EventArgs.Empty);
    }
    

    这将在创建 SmartQueue 实例的同一线程上执行 OnItemAdded 处理程序(订阅者)。

    【讨论】:

      【解决方案3】:

      在主线程中尝试这段代码:

      SmartQueue smartQueue = new SmartQueue();
      
      public Form1()
      {
          InitializeComponent();
          smartQueue.ItemAdded += new SmartQueue.ItemAddedEventHandler(smartQueue_ItemAdded);
      }
      
      void smartQueue_ItemAdded(object sender, EventArgs e)
      {
          // add your code in here
      }
      

      【讨论】:

      • 这是简单的事件订阅,已经由 OP 完成。它是如何解决多线程问题的?
      • 对于多线程问题,您需要在测试和调用之前将委托分配给临时变量,以保证线程安全。可能是这样的:protected virtual void OnItemAdded(EventArgs e){ var temp=ItemAdded; if(temp!=null){ temp(this, e);}}
      猜你喜欢
      • 1970-01-01
      • 2017-06-19
      • 1970-01-01
      • 1970-01-01
      • 2020-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多