如何在已经运行的线程上执行函数?

时间:2017-09-05 08:51:27

标签: c# multithreading

我有一个线程A,它使用一个计时器每5秒调用一次FunctionX()。但有时我需要来自另一个线程,线程B的FunctionX()的值,并且不能等待定时器执行。我无法直接从线程B调用FunctionX(),因为它使用了一些外部组件,如果从另一个线程调用而不是原始组件。因此,FunctionX()必须始终在线程A上运行。如何在线程B上立即获取FunctionX()的值,而无需等待定时器调用该函数?

3 个答案:

答案 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` */ });