Windows 从主线程调用服务方法
Windows Service invoke method from main thread
我想在工作线程的上下文中从主线程执行一个方法,但我不知道该怎么做。
详细:
- 主服务已启动
- 工作线程已启动
- 我想在工作线程中调用一个方法,但不在工作线程上下文中
有人可以提示我如何执行此操作吗?
如果我正确理解了你的问题,你通常会通过获取该线程的 SycnhronizationContext
然后调用 Post
或 Send
来将工作发送给另一个上下文的广告.问题在于,在控制台应用程序和 windows 服务中,默认 SycnhronizationContext
与所有线程池线程关联,因此您发送的工作可以 运行 在任何线程上。
但是 Stephe Toub has an example 如何在特定线程上创建自定义 SycnhronizationContext
到 运行,然后当您发送它时,它将保证 运行 在那个线程上。为了清楚起见,我已将一些代码粘贴到此答案中。
/// <summary>Provides a pump that supports running asynchronous methods on the current thread. </summary>
public static class AsyncPump
{
/// <summary>Runs the specified asynchronous function.</summary>
/// <param name="func">The asynchronous function to execute.</param>
public static void Run(Func<Task> func)
{
if (func == null) throw new ArgumentNullException("func");
var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(syncCtx);
// Invoke the function and alert the context to when it completes
var t = func();
if (t == null) throw new InvalidOperationException("No task provided.");
t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);
// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
t.GetAwaiter().GetResult();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}
/// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
private sealed class SingleThreadSynchronizationContext : SynchronizationContext
{
/// <summary>The queue of work items.</summary>
private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
/// <summary>The processing thread.</summary>
private readonly Thread m_thread = Thread.CurrentThread;
/// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
/// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
/// <param name="state">The object passed to the delegate.</param>
public override void Post(SendOrPostCallback d, object state)
{
if (d == null) throw new ArgumentNullException("d");
m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
}
/// <summary>Not supported.</summary>
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("Synchronously sending is not supported.");
}
/// <summary>Runs an loop to process all queued work items.</summary>
public void RunOnCurrentThread()
{
foreach (var workItem in m_queue.GetConsumingEnumerable())
workItem.Key(workItem.Value);
}
/// <summary>Notifies the context that no more work will arrive.</summary>
public void Complete() { m_queue.CompleteAdding(); }
}
}
因此您需要在主线程上 运行 AsyncPump
并将 SingleThreadSynchronizationContext
交给工作线程,以便它可以将工作发送到主线程。
void Main()
{
AsyncPump.Run(async delegate
{
var syncContext = SynchronizationContext.Current;
Console.WriteLine("Main thread, thradId:{0}", Thread.CurrentThread.ManagedThreadId);
await Task.Run(() =>
{
Console.WriteLine("Background thread, thradId:{0}", Thread.CurrentThread.ManagedThreadId);
syncContext.Post(new SendOrPostCallback((state) =>
{
Console.WriteLine("Running on main thread again, thradId:{0}", Thread.CurrentThread.ManagedThreadId);
}), null);
});
Console.ReadLine();
});;
}
我想在工作线程的上下文中从主线程执行一个方法,但我不知道该怎么做。
详细:
- 主服务已启动
- 工作线程已启动
- 我想在工作线程中调用一个方法,但不在工作线程上下文中
有人可以提示我如何执行此操作吗?
如果我正确理解了你的问题,你通常会通过获取该线程的 SycnhronizationContext
然后调用 Post
或 Send
来将工作发送给另一个上下文的广告.问题在于,在控制台应用程序和 windows 服务中,默认 SycnhronizationContext
与所有线程池线程关联,因此您发送的工作可以 运行 在任何线程上。
但是 Stephe Toub has an example 如何在特定线程上创建自定义 SycnhronizationContext
到 运行,然后当您发送它时,它将保证 运行 在那个线程上。为了清楚起见,我已将一些代码粘贴到此答案中。
/// <summary>Provides a pump that supports running asynchronous methods on the current thread. </summary>
public static class AsyncPump
{
/// <summary>Runs the specified asynchronous function.</summary>
/// <param name="func">The asynchronous function to execute.</param>
public static void Run(Func<Task> func)
{
if (func == null) throw new ArgumentNullException("func");
var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(syncCtx);
// Invoke the function and alert the context to when it completes
var t = func();
if (t == null) throw new InvalidOperationException("No task provided.");
t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);
// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
t.GetAwaiter().GetResult();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}
/// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
private sealed class SingleThreadSynchronizationContext : SynchronizationContext
{
/// <summary>The queue of work items.</summary>
private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
/// <summary>The processing thread.</summary>
private readonly Thread m_thread = Thread.CurrentThread;
/// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
/// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
/// <param name="state">The object passed to the delegate.</param>
public override void Post(SendOrPostCallback d, object state)
{
if (d == null) throw new ArgumentNullException("d");
m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
}
/// <summary>Not supported.</summary>
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("Synchronously sending is not supported.");
}
/// <summary>Runs an loop to process all queued work items.</summary>
public void RunOnCurrentThread()
{
foreach (var workItem in m_queue.GetConsumingEnumerable())
workItem.Key(workItem.Value);
}
/// <summary>Notifies the context that no more work will arrive.</summary>
public void Complete() { m_queue.CompleteAdding(); }
}
}
因此您需要在主线程上 运行 AsyncPump
并将 SingleThreadSynchronizationContext
交给工作线程,以便它可以将工作发送到主线程。
void Main()
{
AsyncPump.Run(async delegate
{
var syncContext = SynchronizationContext.Current;
Console.WriteLine("Main thread, thradId:{0}", Thread.CurrentThread.ManagedThreadId);
await Task.Run(() =>
{
Console.WriteLine("Background thread, thradId:{0}", Thread.CurrentThread.ManagedThreadId);
syncContext.Post(new SendOrPostCallback((state) =>
{
Console.WriteLine("Running on main thread again, thradId:{0}", Thread.CurrentThread.ManagedThreadId);
}), null);
});
Console.ReadLine();
});;
}