问题
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 ?
//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.
回答1:
You are enqueueing items on background thread (DoWork event handler runs on background thread), thus your event raised also in background thread.
Use InvokeRequired
method to verify if you are on UI thread. And if not, then use Invoke
to run code on UI thread:
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 ProgressChanged
event to enqueue items (don't forget to set WorkerReportsProgress
to true). This event handler runs on UI thread:
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);
}
回答2:
You can use the same approach as the BackgroundWorker
, that is incorporating AsyncOperation as a member in your class, which can dispatch operations to a thread it has been created in.
protected AsyncOperation AsyncOp;
Instantiate it in your constructor with "null" argument. That creates an async operation "bound" to the current thread.
public SmartQueue()
{
AsyncOp = AsyncOperationManager.CreateOperation(null);
}
Then you can use the AsyncOp to Post
your event.
public override void Enqueue(object Item)
{
base.Enqueue(Item);
AsyncOp.Post(OnItemAdded, EventArgs.Empty);
}
That will execute the OnItemAdded
handlers (subscribers) on the same thread that created the SmartQueue instance.
回答3:
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
}
来源:https://stackoverflow.com/questions/13698704/execute-a-method-in-main-thread-from-event-handler