2017-09-05 40 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` */ }); 
相關問題