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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13698704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 09:27:56  来源:igfitidea点击:

Execute a method in main thread from event handler

c#winformsmultithreadingeventsbackgroundworker

提问by s.k.paul

I have a custom Queue class inherited from Queue class. It has an event ItemAdded. In the event handler of this event i am executing a method. But it is running other than main thread, though i want it in main thread. I don't know how to do it. Any suggestion ?

我有一个从 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();
    }
}
}

Thanks in advance. SKPaul.

提前致谢。SK保罗。

采纳答案by Sergey Berezovskiy

You are enqueueing items on background thread (DoWork event handler runs on background thread), thus your event raised also in background thread.

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

Use InvokeRequiredmethod to verify if you are on UI thread. And if not, then use Invoketo run code on UI thread:

使用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();
 }

Another option for you - use ProgressChangedevent to enqueue items (don't forget to set WorkerReportsProgressto true). This event handler runs on UI thread:

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

回答by Honza Brestan

You can use the same approach as the BackgroundWorker, that is incorporating AsyncOperationas a member in your class, which can dispatch operations to a thread it has been created in.

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

protected AsyncOperation AsyncOp;

Instantiate it in your constructor with "null" argument. That creates an async operation "bound" to the current thread.

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

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

Then you can use the AsyncOp to Postyour event.

然后您可以将 AsyncOp 用于Post您的事件。

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

That will execute the OnItemAddedhandlers (subscribers) on the same thread that created the SmartQueue instance.

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

回答by YD4

Try with this code in main thread:

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

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
}