2017-09-05 41 views
3

我有线程A每5秒钟用一个定时器调用FunctionX()。但是有时我需要立即从另一个线程(线程B)获得FunctionX()的值,并且不能等待定时器执行。我无法直接从线程B调用FunctionX(),因为它使用了一些外部组件,如果从另一个线程调用而不是原始内容,那么这些组件会死掉。所以FunctionX()必须总是在线程A上运行。如何在线程B上立即获取FunctionX()的值,而无需等待定时器调用该函数?如何在已经运行的线程上执行一个函数?

+2

某种类型的生产者 - 消费者模式,其中定时器推动价值和你直接调用推动它为好,其中FunctionX()将其结果推送到另一个队列? – CodeCaster

+0

您是否熟悉Taskbased异步模式?你可以有一个专用线程等待一个阻塞队列。您将TaskCompletionSource对象排入该队列。在线程中,它将取消它,调用FunctionX并将CompletionSource结果设置为函数的结果......只是一个想法。 – Fildor

回答

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); 
     } 
    } 
} 

你会看到的输出看起来是这样的:

2017年5月9日10时04分44秒:做的东西

2017年5月9日10时04分49秒:做的东西

2017年5月9日10时04分54秒:做的东西

2017年5月9日10时04分59秒:做的东西

2017年5月9日10时05分04秒:做的东西

2017年5月9日10时05分09秒:做的东西

2017年5月9日10时05分09秒:做东西

2017年5月9日10时05分09秒:做的东西

2017年5月9日10时05分09秒:做的东西

2017年5月9日10时05分09秒:做东西

因此,计时器每5秒钟触发一次(如预期的),然后每隔100毫秒开始触发一次。 “一经请求”)。这个代码坐在一个人为的测试工具,所以看起来有点奇怪,但其目的基本上是向您展示调用Change方法的结果。

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线程。

0

下面介绍如何轻松地与微软的反应框架(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秒之间的值和之间的值,并且这是标记“值”的唯一值。其他人有“计时器”。

如果您有什么需要运行在threadA只是这样做:

threadA.Schedule(() => { /* Do this on `threadA` */ }); 
相关问题