I have a console application which talks to an external library. Unfortunately all calls to the library must be made from the same thread.
How can I send method calls from one thread to another? (And, obviously, send the method results back to the calling thread.)
(No, this isn't to do with GUI programming. No, using the GUI message pump won't work.)
What I really want is for every single method on a particular class to always be executed in the same thread. But I have no idea how to do that.
My advice is to do what Windows Forms and WPF do to set up their single threaded message pumps - inherit SynchronizationContext
. http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext%28v=vs.110%29.aspx
In your implementation, you will need to maintain a thread safe message queue, similar to this one: http://www.codeproject.com/Articles/56369/Thread-safe-priority-queue-in-C Your message pump worker thread will constantly check for new delegates, and invoke them.
So why not just write a message pump?
Well, by inheriting SynchronizationContext
, you get all the CLR goodies like BackgroundWorker
, AsyncOperationManager
and the new await/async
pattern keyword for free! They will all magically join back to your library thread.
Here is some code for a basic message pump. It does not implement SynchronizationContext
:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace MessagePump
{
class Program
{
static void Main(string[] args)
{
MessagePump p = new MessagePump();
p.Start();
p.AddMessage(() => Console.WriteLine("message 1"));
p.AddMessage(() => Console.WriteLine("message 2"));
p.AddMessage(() => Console.WriteLine("message 3"));
Console.ReadLine();
p.Stop();
}
}
class MessagePump
{
bool m_Working = false;
Queue<Action> m_Actions = new Queue<Action>();
public void Start()
{
m_Working = true;
Thread t = new Thread(DoPump);
t.Name = "Message Pump Thread";
t.Start();
}
void DoPump()
{
while (m_Working)
{
try
{
Monitor.Enter(m_Actions);
while (m_Actions.Count > 0)
{
m_Actions.Dequeue()(); //dequeue and invoke a delegate
}
}
finally
{
Monitor.Exit(m_Actions);
}
Thread.Sleep(100); //dont want to lock this core!
}
}
public void Stop()
{
m_Working = false;
}
public void AddMessage(Action act)
{
lock (m_Actions)
{
m_Actions.Enqueue(act);
}
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.