我有一个线程A,它使用一个计时器每5秒调用一次FunctionX()。但有时我需要来自另一个线程,线程B的FunctionX()的值,并且不能等待定时器执行。我无法直接从线程B调用FunctionX(),因为它使用了一些外部组件,如果从另一个线程调用而不是原始组件。因此,FunctionX()必须始终在线程A上运行。如何在线程B上立即获取FunctionX()的值,而无需等待定时器调用该函数?
答案 0 :(得分:1)
这将取决于您正在使用的计时器类型,但System.Threading.Timer
类作为一个示例,公开了一个Change
方法,您可以用它来说服计时器现在开火。以下是控制台应用程序测试工具中的示例:
using System;
using System.Threading;
namespace FiringTheTimerTestHarness
{
class Program
{
public static Thread _worker;
public static Timer _timer;
static void Main(string[] args)
{
_worker = new Thread(new ThreadStart(ThreadWorker));
_worker.Start();
var startTime = DateTime.Now;
// Simulate the main UI thread being active doing stuff (i.e. if there's a Windows Forms app so we don't need anything to
// keep the app "alive"
while (1==1)
{
Thread.Sleep(100);
if (startTime.AddSeconds(30) < DateTime.Now)
{
// Lets pretend that we need to fire the timer *now* so that we can get the result *now*
_timer.Change(0, 5000);
}
}
}
public static void ThreadWorker()
{
_timer = new Timer(new TimerCallback(DoStuffEveryFiveSeconds), null, 5000, 5000);
while (1 == 1)
{
Thread.Sleep(100);
}
}
public static void DoStuffEveryFiveSeconds(object state)
{
Console.WriteLine("{0}: Doing stuff", DateTime.Now);
}
}
}
您将看到类似于以下内容的输出:
05/09/2017 10:04:44:做点什么
05/09/2017 10:04:49:做点什么
05/09/2017 10:04:54:做点什么
05/09/2017 10:04:59:做点什么
05/09/2017 10:05:04:做点什么
05/09/2017 10:05:09:做点什么
05/09/2017 10:05:09:做点什么
05/09/2017 10:05:09:做点什么
05/09/2017 10:05:09:做点什么
05/09/2017 10:05:09:做点什么
因此,计时器每五秒触发一次(按预期),然后每100毫秒开始一次点火(即#34;按需扫描&#34;)。这段代码位于一个人为的测试工具中,所以看起来有点奇怪,但其目的主要是通过调用Change
方法向您展示结果。
答案 1 :(得分:0)
以下是评论中我所谈论的概要。 完全未经测试 !!
class FunctionXCaller
{
private Task mTask;
private BlockingCollection<TaskCompletionSource<TResult>> queue = new BlockingCollection<TaskCompletionSource<TResult>>();
public FunctionXCaller()
{
mTask = Task.Run( () => WorkerMethod );
}
private void WorkerMethod()
{
while( !queue.IsCompleted )
{
TaskCompletionSource<TResult> tcs = queue.take();
tcs.TrySetResult(FunctionX());
}
}
public Task<TResult> CallXAsync()
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
queue.Add(tcs);
return tcs.Task;
}
}
注意:正如我已经写过的,这只是为了让你有个主意。需要添加很多东西,比如关闭,清理,异常处理......
这个想法是你可以从任何线程调用await FunctionXCallerInstance.CallXAsnc()
并且总是在工作线程上执行FunctionX。这里将是一个ThreadPool线程。
答案 2 :(得分:0)
以下是如何使用Microsoft的Reactive Framework(Rx)轻松完成此操作:
void Main()
{
EventLoopScheduler threadA = new EventLoopScheduler();
IObservable<int> timer = Observable.Interval(TimeSpan.FromSeconds(5.0), threadA).Select(x => FunctionX());
IDisposable subscription = timer.Subscribe(x => Console.WriteLine("timer: " + x));
Thread.Sleep(TimeSpan.FromSeconds(12.5));
int value = Observable.Start(() => FunctionX(), threadA).Wait();
Console.WriteLine("value: " + value);
}
private int counter = 0;
public int FunctionX()
{
Console.Write("ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId + "; ");
Console.Write(DateTime.Now.ToString("ss.ffff") + "; ");
return ++counter;
}
此测试代码的输出为:
ManagedThreadId: 13; 47.5655; timer: 1 ManagedThreadId: 13; 52.5601; timer: 2 ManagedThreadId: 13; 55.0649; value: 3 ManagedThreadId: 13; 57.5592; timer: 4 ManagedThreadId: 13; 02.5594; timer: 5 ManagedThreadId: 13; 07.5741; timer: 6
请注意,第三个值在前一个值和后一个值之间获得2.5秒,并且这是唯一带有标记&#34; value&#34;的值。其他人有&#34;计时器&#34;。
如果您需要在threadA
上运行其他任何内容,请执行以下操作:
threadA.Schedule(() => { /* Do this on `threadA` */ });