从事件处理程序中在主线程中执行一个方法

13

我有一个继承自队列类的自定义队列类,它具有一个名为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();
    }
}
}

提前致谢。 SKPaul。

3个回答

27

您正在后台线程上排队项目(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 线程上运行:

另一个选择 - 使用 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);
}

14
如果您的主线程没有Form窗体,例如托盘应用程序的ApplicationContext类,那么Invoke方法将无法使用。请问是否需要进一步翻译? - xr280xr

2
你可以采用与BackgroundWorker相同的方法,即将AsyncOperation作为类成员,它可以将操作分派到创建它的线程中。请注意保留HTML标签。
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处理程序(订阅者)。


-2

在主线程中尝试使用以下代码:

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 完成。它如何解决多线程问题? - Sergey Berezovskiy
对于多线程问题,您需要在测试和调用委托之前将其分配给临时变量,以确保线程安全。可能像这样:protected virtual void OnItemAdded(EventArgs e){ var temp=ItemAdded; if(temp!=null){ temp(this, e);}} - YD4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接